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