diff options
author | Wolfgang Grandegger <wg@grandegger.com> | 2009-05-30 03:55:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-06-01 05:53:35 -0400 |
commit | d1a277c584d0862dbf51991baea947ea5f2ce6bf (patch) | |
tree | 368832ff703abde6c813eaaba42a48ae451fc1f1 /drivers | |
parent | 255a9154319d3cf475d527458037758935f6445b (diff) |
can: sja1000: generic OF platform bus driver
This patch adds a generic driver for SJA1000 chips on the OpenFirmware
platform bus found on embedded PowerPC systems. You need a SJA1000 node
definition in your flattened device tree source (DTS) file similar to:
can@3,100 {
compatible = "nxp,sja1000";
reg = <3 0x100 0x80>;
interrupts = <2 0>;
interrupt-parent = <&mpic>;
nxp,external-clock-frequency = <16000000>;
};
See also Documentation/powerpc/dts-bindings/can/sja1000.txt.
CC: devicetree-discuss@ozlabs.org
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/can/Kconfig | 9 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000_of_platform.c | 233 |
3 files changed, 243 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index cfd6c5a285fa..d5e18812bf49 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig | |||
@@ -51,6 +51,15 @@ config CAN_SJA1000_PLATFORM | |||
51 | boards from Phytec (http://www.phytec.de) like the PCM027, | 51 | boards from Phytec (http://www.phytec.de) like the PCM027, |
52 | PCM038. | 52 | PCM038. |
53 | 53 | ||
54 | config CAN_SJA1000_OF_PLATFORM | ||
55 | depends on CAN_SJA1000 && PPC_OF | ||
56 | tristate "Generic OF Platform Bus based SJA1000 driver" | ||
57 | ---help--- | ||
58 | This driver adds support for the SJA1000 chips connected to | ||
59 | the OpenFirmware "platform bus" found on embedded systems with | ||
60 | OpenFirmware bindings, e.g. if you have a PowerPC based system | ||
61 | you may want to enable this option. | ||
62 | |||
54 | config CAN_EMS_PCI | 63 | config CAN_EMS_PCI |
55 | tristate "EMS CPC-PCI and CPC-PCIe Card" | 64 | tristate "EMS CPC-PCI and CPC-PCIe Card" |
56 | depends on PCI && CAN_SJA1000 | 65 | depends on PCI && CAN_SJA1000 |
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index d6c631f9e665..9d0c08da273c 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o | 5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o |
6 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o | 6 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o |
7 | obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o | ||
7 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o | 8 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o |
8 | obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o | 9 | obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o |
9 | 10 | ||
diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c new file mode 100644 index 000000000000..aa953fb4b8d0 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_of_platform.c | |||
@@ -0,0 +1,233 @@ | |||
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); | ||