]>
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 | * | |
32 | * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further | |
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> | |
41 | #include <linux/can.h> | |
42 | #include <linux/can/dev.h> | |
43 | ||
44 | #include <linux/of_platform.h> | |
45 | #include <asm/prom.h> | |
46 | ||
47 | #include "sja1000.h" | |
48 | ||
49 | #define DRV_NAME "sja1000_of_platform" | |
50 | ||
51 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | |
52 | MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); | |
53 | MODULE_LICENSE("GPL v2"); | |
54 | ||
55 | #define SJA1000_OFP_CAN_CLOCK (16000000 / 2) | |
56 | ||
57 | #define SJA1000_OFP_OCR OCR_TX0_PULLDOWN | |
58 | #define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) | |
59 | ||
60 | static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) | |
61 | { | |
62 | return in_8(priv->reg_base + reg); | |
63 | } | |
64 | ||
65 | static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, | |
66 | int reg, u8 val) | |
67 | { | |
68 | out_8(priv->reg_base + reg, val); | |
69 | } | |
70 | ||
71 | static int __devexit sja1000_ofp_remove(struct of_device *ofdev) | |
72 | { | |
73 | struct net_device *dev = dev_get_drvdata(&ofdev->dev); | |
74 | struct sja1000_priv *priv = netdev_priv(dev); | |
75 | struct device_node *np = ofdev->node; | |
76 | struct resource res; | |
77 | ||
78 | dev_set_drvdata(&ofdev->dev, NULL); | |
79 | ||
80 | unregister_sja1000dev(dev); | |
81 | free_sja1000dev(dev); | |
82 | iounmap(priv->reg_base); | |
83 | irq_dispose_mapping(dev->irq); | |
84 | ||
85 | of_address_to_resource(np, 0, &res); | |
86 | release_mem_region(res.start, resource_size(&res)); | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | static int __devinit sja1000_ofp_probe(struct of_device *ofdev, | |
92 | const struct of_device_id *id) | |
93 | { | |
94 | struct device_node *np = ofdev->node; | |
95 | struct net_device *dev; | |
96 | struct sja1000_priv *priv; | |
97 | struct resource res; | |
98 | const u32 *prop; | |
99 | int err, irq, res_size, prop_size; | |
100 | void __iomem *base; | |
101 | ||
102 | err = of_address_to_resource(np, 0, &res); | |
103 | if (err) { | |
104 | dev_err(&ofdev->dev, "invalid address\n"); | |
105 | return err; | |
106 | } | |
107 | ||
108 | res_size = resource_size(&res); | |
109 | ||
110 | if (!request_mem_region(res.start, res_size, DRV_NAME)) { | |
111 | dev_err(&ofdev->dev, "couldn't request %#x..%#x\n", | |
112 | res.start, res.end); | |
113 | return -EBUSY; | |
114 | } | |
115 | ||
116 | base = ioremap_nocache(res.start, res_size); | |
117 | if (!base) { | |
118 | dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n", | |
119 | res.start, res.end); | |
120 | err = -ENOMEM; | |
121 | goto exit_release_mem; | |
122 | } | |
123 | ||
124 | irq = irq_of_parse_and_map(np, 0); | |
125 | if (irq == NO_IRQ) { | |
126 | dev_err(&ofdev->dev, "no irq found\n"); | |
127 | err = -ENODEV; | |
128 | goto exit_unmap_mem; | |
129 | } | |
130 | ||
131 | dev = alloc_sja1000dev(0); | |
132 | if (!dev) { | |
133 | err = -ENOMEM; | |
134 | goto exit_dispose_irq; | |
135 | } | |
136 | ||
137 | priv = netdev_priv(dev); | |
138 | ||
139 | priv->read_reg = sja1000_ofp_read_reg; | |
140 | priv->write_reg = sja1000_ofp_write_reg; | |
141 | ||
142 | prop = of_get_property(np, "nxp,external-clock-frequency", &prop_size); | |
143 | if (prop && (prop_size == sizeof(u32))) | |
144 | priv->can.clock.freq = *prop / 2; | |
145 | else | |
146 | priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ | |
147 | ||
148 | prop = of_get_property(np, "nxp,tx-output-mode", &prop_size); | |
149 | if (prop && (prop_size == sizeof(u32))) | |
150 | priv->ocr |= *prop & OCR_MODE_MASK; | |
151 | else | |
152 | priv->ocr |= OCR_MODE_NORMAL; /* default */ | |
153 | ||
154 | prop = of_get_property(np, "nxp,tx-output-config", &prop_size); | |
155 | if (prop && (prop_size == sizeof(u32))) | |
156 | priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK; | |
157 | else | |
158 | priv->ocr |= OCR_TX0_PULLDOWN; /* default */ | |
159 | ||
160 | prop = of_get_property(np, "nxp,clock-out-frequency", &prop_size); | |
161 | if (prop && (prop_size == sizeof(u32)) && *prop) { | |
162 | u32 divider = priv->can.clock.freq * 2 / *prop; | |
163 | ||
164 | if (divider > 1) | |
165 | priv->cdr |= divider / 2 - 1; | |
166 | else | |
167 | priv->cdr |= CDR_CLKOUT_MASK; | |
168 | } else { | |
169 | priv->cdr |= CDR_CLK_OFF; /* default */ | |
170 | } | |
171 | ||
172 | prop = of_get_property(np, "nxp,no-comparator-bypass", NULL); | |
173 | if (!prop) | |
174 | priv->cdr |= CDR_CBP; /* default */ | |
175 | ||
176 | priv->irq_flags = IRQF_SHARED; | |
177 | priv->reg_base = base; | |
178 | ||
179 | dev->irq = irq; | |
180 | ||
181 | dev_info(&ofdev->dev, | |
182 | "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", | |
183 | priv->reg_base, dev->irq, priv->can.clock.freq, | |
184 | priv->ocr, priv->cdr); | |
185 | ||
186 | dev_set_drvdata(&ofdev->dev, dev); | |
187 | SET_NETDEV_DEV(dev, &ofdev->dev); | |
188 | ||
189 | err = register_sja1000dev(dev); | |
190 | if (err) { | |
191 | dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", | |
192 | DRV_NAME, err); | |
193 | goto exit_free_sja1000; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | ||
198 | exit_free_sja1000: | |
199 | free_sja1000dev(dev); | |
200 | exit_dispose_irq: | |
201 | irq_dispose_mapping(irq); | |
202 | exit_unmap_mem: | |
203 | iounmap(base); | |
204 | exit_release_mem: | |
205 | release_mem_region(res.start, res_size); | |
206 | ||
207 | return err; | |
208 | } | |
209 | ||
210 | static struct of_device_id __devinitdata sja1000_ofp_table[] = { | |
211 | {.compatible = "nxp,sja1000"}, | |
212 | {}, | |
213 | }; | |
214 | ||
215 | static struct of_platform_driver sja1000_ofp_driver = { | |
216 | .owner = THIS_MODULE, | |
217 | .name = DRV_NAME, | |
218 | .probe = sja1000_ofp_probe, | |
219 | .remove = __devexit_p(sja1000_ofp_remove), | |
220 | .match_table = sja1000_ofp_table, | |
221 | }; | |
222 | ||
223 | static int __init sja1000_ofp_init(void) | |
224 | { | |
225 | return of_register_platform_driver(&sja1000_ofp_driver); | |
226 | } | |
227 | module_init(sja1000_ofp_init); | |
228 | ||
229 | static void __exit sja1000_ofp_exit(void) | |
230 | { | |
231 | return of_unregister_platform_driver(&sja1000_ofp_driver); | |
232 | }; | |
233 | module_exit(sja1000_ofp_exit); |