Commit | Line | Data |
---|---|---|
d1a277c5 WG |
1 | /* |
2 | * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus | |
3 | * | |
4 | * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the version 2 of the GNU General Public License | |
8 | * as published by the Free Software Foundation | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software Foundation, | |
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | */ | |
19 | ||
20 | /* This is a generic driver for SJA1000 chips on the OpenFirmware platform | |
21 | * bus found on embedded PowerPC systems. You need a SJA1000 CAN node | |
22 | * definition in your flattened device tree source (DTS) file similar to: | |
23 | * | |
24 | * can@3,100 { | |
25 | * compatible = "nxp,sja1000"; | |
26 | * reg = <3 0x100 0x80>; | |
27 | * interrupts = <2 0>; | |
28 | * interrupt-parent = <&mpic>; | |
29 | * nxp,external-clock-frequency = <16000000>; | |
30 | * }; | |
31 | * | |
395cf969 | 32 | * See "Documentation/devicetree/bindings/net/can/sja1000.txt" for further |
d1a277c5 WG |
33 | * information. |
34 | */ | |
35 | ||
36 | #include <linux/kernel.h> | |
37 | #include <linux/module.h> | |
38 | #include <linux/interrupt.h> | |
39 | #include <linux/netdevice.h> | |
40 | #include <linux/delay.h> | |
8d0f7cea | 41 | #include <linux/io.h> |
d1a277c5 WG |
42 | #include <linux/can/dev.h> |
43 | ||
44 | #include <linux/of_platform.h> | |
04df2510 AL |
45 | #include <linux/of_address.h> |
46 | #include <linux/of_irq.h> | |
d1a277c5 WG |
47 | #include <asm/prom.h> |
48 | ||
49 | #include "sja1000.h" | |
50 | ||
51 | #define DRV_NAME "sja1000_of_platform" | |
52 | ||
53 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | |
54 | MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); | |
55 | MODULE_LICENSE("GPL v2"); | |
56 | ||
57 | #define SJA1000_OFP_CAN_CLOCK (16000000 / 2) | |
58 | ||
59 | #define SJA1000_OFP_OCR OCR_TX0_PULLDOWN | |
60 | #define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) | |
61 | ||
62 | static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) | |
63 | { | |
04df2510 | 64 | return ioread8(priv->reg_base + reg); |
d1a277c5 WG |
65 | } |
66 | ||
67 | static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, | |
68 | int reg, u8 val) | |
69 | { | |
04df2510 | 70 | iowrite8(val, priv->reg_base + reg); |
d1a277c5 WG |
71 | } |
72 | ||
3c8ac0f2 | 73 | static int sja1000_ofp_remove(struct platform_device *ofdev) |
d1a277c5 WG |
74 | { |
75 | struct net_device *dev = dev_get_drvdata(&ofdev->dev); | |
76 | struct sja1000_priv *priv = netdev_priv(dev); | |
61c7a080 | 77 | struct device_node *np = ofdev->dev.of_node; |
d1a277c5 WG |
78 | struct resource res; |
79 | ||
80 | dev_set_drvdata(&ofdev->dev, NULL); | |
81 | ||
82 | unregister_sja1000dev(dev); | |
83 | free_sja1000dev(dev); | |
84 | iounmap(priv->reg_base); | |
85 | irq_dispose_mapping(dev->irq); | |
86 | ||
87 | of_address_to_resource(np, 0, &res); | |
88 | release_mem_region(res.start, resource_size(&res)); | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
3c8ac0f2 | 93 | static int sja1000_ofp_probe(struct platform_device *ofdev) |
d1a277c5 | 94 | { |
61c7a080 | 95 | struct device_node *np = ofdev->dev.of_node; |
d1a277c5 WG |
96 | struct net_device *dev; |
97 | struct sja1000_priv *priv; | |
98 | struct resource res; | |
0443de5f CF |
99 | u32 prop; |
100 | int err, irq, res_size; | |
d1a277c5 WG |
101 | void __iomem *base; |
102 | ||
103 | err = of_address_to_resource(np, 0, &res); | |
104 | if (err) { | |
105 | dev_err(&ofdev->dev, "invalid address\n"); | |
106 | return err; | |
107 | } | |
108 | ||
109 | res_size = resource_size(&res); | |
110 | ||
111 | if (!request_mem_region(res.start, res_size, DRV_NAME)) { | |
b1323c8f | 112 | dev_err(&ofdev->dev, "couldn't request %pR\n", &res); |
d1a277c5 WG |
113 | return -EBUSY; |
114 | } | |
115 | ||
116 | base = ioremap_nocache(res.start, res_size); | |
117 | if (!base) { | |
b1323c8f | 118 | dev_err(&ofdev->dev, "couldn't ioremap %pR\n", &res); |
d1a277c5 WG |
119 | err = -ENOMEM; |
120 | goto exit_release_mem; | |
121 | } | |
122 | ||
123 | irq = irq_of_parse_and_map(np, 0); | |
50077cd2 | 124 | if (irq == 0) { |
d1a277c5 WG |
125 | dev_err(&ofdev->dev, "no irq found\n"); |
126 | err = -ENODEV; | |
127 | goto exit_unmap_mem; | |
128 | } | |
129 | ||
130 | dev = alloc_sja1000dev(0); | |
131 | if (!dev) { | |
132 | err = -ENOMEM; | |
133 | goto exit_dispose_irq; | |
134 | } | |
135 | ||
136 | priv = netdev_priv(dev); | |
137 | ||
138 | priv->read_reg = sja1000_ofp_read_reg; | |
139 | priv->write_reg = sja1000_ofp_write_reg; | |
140 | ||
0443de5f CF |
141 | err = of_property_read_u32(np, "nxp,external-clock-frequency", &prop); |
142 | if (!err) | |
143 | priv->can.clock.freq = prop / 2; | |
d1a277c5 WG |
144 | else |
145 | priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ | |
146 | ||
0443de5f CF |
147 | err = of_property_read_u32(np, "nxp,tx-output-mode", &prop); |
148 | if (!err) | |
149 | priv->ocr |= prop & OCR_MODE_MASK; | |
d1a277c5 WG |
150 | else |
151 | priv->ocr |= OCR_MODE_NORMAL; /* default */ | |
152 | ||
0443de5f CF |
153 | err = of_property_read_u32(np, "nxp,tx-output-config", &prop); |
154 | if (!err) | |
155 | priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK; | |
d1a277c5 WG |
156 | else |
157 | priv->ocr |= OCR_TX0_PULLDOWN; /* default */ | |
158 | ||
0443de5f CF |
159 | err = of_property_read_u32(np, "nxp,clock-out-frequency", &prop); |
160 | if (!err && prop) { | |
161 | u32 divider = priv->can.clock.freq * 2 / prop; | |
d1a277c5 WG |
162 | |
163 | if (divider > 1) | |
164 | priv->cdr |= divider / 2 - 1; | |
165 | else | |
166 | priv->cdr |= CDR_CLKOUT_MASK; | |
167 | } else { | |
168 | priv->cdr |= CDR_CLK_OFF; /* default */ | |
169 | } | |
170 | ||
0443de5f | 171 | if (!of_property_read_bool(np, "nxp,no-comparator-bypass")) |
d1a277c5 WG |
172 | priv->cdr |= CDR_CBP; /* default */ |
173 | ||
174 | priv->irq_flags = IRQF_SHARED; | |
175 | priv->reg_base = base; | |
176 | ||
177 | dev->irq = irq; | |
178 | ||
179 | dev_info(&ofdev->dev, | |
180 | "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", | |
181 | priv->reg_base, dev->irq, priv->can.clock.freq, | |
182 | priv->ocr, priv->cdr); | |
183 | ||
184 | dev_set_drvdata(&ofdev->dev, dev); | |
185 | SET_NETDEV_DEV(dev, &ofdev->dev); | |
186 | ||
187 | err = register_sja1000dev(dev); | |
188 | if (err) { | |
189 | dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", | |
190 | DRV_NAME, err); | |
191 | goto exit_free_sja1000; | |
192 | } | |
193 | ||
194 | return 0; | |
195 | ||
196 | exit_free_sja1000: | |
197 | free_sja1000dev(dev); | |
198 | exit_dispose_irq: | |
199 | irq_dispose_mapping(irq); | |
200 | exit_unmap_mem: | |
201 | iounmap(base); | |
202 | exit_release_mem: | |
203 | release_mem_region(res.start, res_size); | |
204 | ||
205 | return err; | |
206 | } | |
207 | ||
3c8ac0f2 | 208 | static struct of_device_id sja1000_ofp_table[] = { |
d1a277c5 WG |
209 | {.compatible = "nxp,sja1000"}, |
210 | {}, | |
211 | }; | |
e72701ac | 212 | MODULE_DEVICE_TABLE(of, sja1000_ofp_table); |
d1a277c5 | 213 | |
74888760 | 214 | static struct platform_driver sja1000_ofp_driver = { |
4018294b GL |
215 | .driver = { |
216 | .owner = THIS_MODULE, | |
217 | .name = DRV_NAME, | |
218 | .of_match_table = sja1000_ofp_table, | |
219 | }, | |
d1a277c5 | 220 | .probe = sja1000_ofp_probe, |
3c8ac0f2 | 221 | .remove = sja1000_ofp_remove, |
d1a277c5 WG |
222 | }; |
223 | ||
871d3372 | 224 | module_platform_driver(sja1000_ofp_driver); |