diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/can/cc770/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/can/cc770/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/cc770/cc770_platform.c | 272 |
3 files changed, 280 insertions, 0 deletions
diff --git a/drivers/net/can/cc770/Kconfig b/drivers/net/can/cc770/Kconfig index 28e4d4810f44..22c07a8c8b43 100644 --- a/drivers/net/can/cc770/Kconfig +++ b/drivers/net/can/cc770/Kconfig | |||
@@ -11,4 +11,11 @@ config CAN_CC770_ISA | |||
11 | connected to the ISA bus using I/O port, memory mapped or | 11 | connected to the ISA bus using I/O port, memory mapped or |
12 | indirect access. | 12 | indirect access. |
13 | 13 | ||
14 | config CAN_CC770_PLATFORM | ||
15 | tristate "Generic Platform Bus based CC770 driver" | ||
16 | ---help--- | ||
17 | This driver adds support for the CC770 and AN82527 chips | ||
18 | connected to the "platform bus" (Linux abstraction for directly | ||
19 | to the processor attached devices). | ||
20 | |||
14 | endif | 21 | endif |
diff --git a/drivers/net/can/cc770/Makefile b/drivers/net/can/cc770/Makefile index 872ecffa80b7..9fb8321b33eb 100644 --- a/drivers/net/can/cc770/Makefile +++ b/drivers/net/can/cc770/Makefile | |||
@@ -4,5 +4,6 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_CAN_CC770) += cc770.o | 5 | obj-$(CONFIG_CAN_CC770) += cc770.o |
6 | obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o | 6 | obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o |
7 | obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o | ||
7 | 8 | ||
8 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG | 9 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG |
diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c new file mode 100644 index 000000000000..53115eee8075 --- /dev/null +++ b/drivers/net/can/cc770/cc770_platform.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Driver for CC770 and AN82527 CAN controllers on the platform bus | ||
3 | * | ||
4 | * Copyright (C) 2009, 2011 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 | |||
16 | /* | ||
17 | * If platform data are used you should have similar definitions | ||
18 | * in your board-specific code: | ||
19 | * | ||
20 | * static struct cc770_platform_data myboard_cc770_pdata = { | ||
21 | * .osc_freq = 16000000, | ||
22 | * .cir = 0x41, | ||
23 | * .cor = 0x20, | ||
24 | * .bcr = 0x40, | ||
25 | * }; | ||
26 | * | ||
27 | * Please see include/linux/can/platform/cc770.h for description of | ||
28 | * above fields. | ||
29 | * | ||
30 | * If the device tree is used, you need a CAN node definition in your | ||
31 | * DTS file similar to: | ||
32 | * | ||
33 | * can@3,100 { | ||
34 | * compatible = "bosch,cc770"; | ||
35 | * reg = <3 0x100 0x80>; | ||
36 | * interrupts = <2 0>; | ||
37 | * interrupt-parent = <&mpic>; | ||
38 | * bosch,external-clock-frequency = <16000000>; | ||
39 | * }; | ||
40 | * | ||
41 | * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further | ||
42 | * information. | ||
43 | */ | ||
44 | |||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/module.h> | ||
47 | #include <linux/interrupt.h> | ||
48 | #include <linux/netdevice.h> | ||
49 | #include <linux/delay.h> | ||
50 | #include <linux/platform_device.h> | ||
51 | #include <linux/of.h> | ||
52 | #include <linux/can.h> | ||
53 | #include <linux/can/dev.h> | ||
54 | #include <linux/can/platform/cc770.h> | ||
55 | |||
56 | #include "cc770.h" | ||
57 | |||
58 | #define DRV_NAME "cc770_platform" | ||
59 | |||
60 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | ||
61 | MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus"); | ||
62 | MODULE_LICENSE("GPL v2"); | ||
63 | |||
64 | #define CC770_PLATFORM_CAN_CLOCK 16000000 | ||
65 | |||
66 | static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg) | ||
67 | { | ||
68 | return ioread8(priv->reg_base + reg); | ||
69 | } | ||
70 | |||
71 | static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg, | ||
72 | u8 val) | ||
73 | { | ||
74 | iowrite8(val, priv->reg_base + reg); | ||
75 | } | ||
76 | |||
77 | static int __devinit cc770_get_of_node_data(struct platform_device *pdev, | ||
78 | struct cc770_priv *priv) | ||
79 | { | ||
80 | struct device_node *np = pdev->dev.of_node; | ||
81 | const u32 *prop; | ||
82 | int prop_size; | ||
83 | u32 clkext; | ||
84 | |||
85 | prop = of_get_property(np, "bosch,external-clock-frequency", | ||
86 | &prop_size); | ||
87 | if (prop && (prop_size == sizeof(u32))) | ||
88 | clkext = *prop; | ||
89 | else | ||
90 | clkext = CC770_PLATFORM_CAN_CLOCK; /* default */ | ||
91 | priv->can.clock.freq = clkext; | ||
92 | |||
93 | /* The system clock may not exceed 10 MHz */ | ||
94 | if (priv->can.clock.freq > 10000000) { | ||
95 | priv->cpu_interface |= CPUIF_DSC; | ||
96 | priv->can.clock.freq /= 2; | ||
97 | } | ||
98 | |||
99 | /* The memory clock may not exceed 8 MHz */ | ||
100 | if (priv->can.clock.freq > 8000000) | ||
101 | priv->cpu_interface |= CPUIF_DMC; | ||
102 | |||
103 | if (of_get_property(np, "bosch,divide-memory-clock", NULL)) | ||
104 | priv->cpu_interface |= CPUIF_DMC; | ||
105 | if (of_get_property(np, "bosch,iso-low-speed-mux", NULL)) | ||
106 | priv->cpu_interface |= CPUIF_MUX; | ||
107 | |||
108 | if (!of_get_property(np, "bosch,no-comperator-bypass", NULL)) | ||
109 | priv->bus_config |= BUSCFG_CBY; | ||
110 | if (of_get_property(np, "bosch,disconnect-rx0-input", NULL)) | ||
111 | priv->bus_config |= BUSCFG_DR0; | ||
112 | if (of_get_property(np, "bosch,disconnect-rx1-input", NULL)) | ||
113 | priv->bus_config |= BUSCFG_DR1; | ||
114 | if (of_get_property(np, "bosch,disconnect-tx1-output", NULL)) | ||
115 | priv->bus_config |= BUSCFG_DT1; | ||
116 | if (of_get_property(np, "bosch,polarity-dominant", NULL)) | ||
117 | priv->bus_config |= BUSCFG_POL; | ||
118 | |||
119 | prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size); | ||
120 | if (prop && (prop_size == sizeof(u32)) && *prop > 0) { | ||
121 | u32 cdv = clkext / *prop; | ||
122 | int slew; | ||
123 | |||
124 | if (cdv > 0 && cdv < 16) { | ||
125 | priv->cpu_interface |= CPUIF_CEN; | ||
126 | priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK; | ||
127 | |||
128 | prop = of_get_property(np, "bosch,slew-rate", | ||
129 | &prop_size); | ||
130 | if (prop && (prop_size == sizeof(u32))) { | ||
131 | slew = *prop; | ||
132 | } else { | ||
133 | /* Determine default slew rate */ | ||
134 | slew = (CLKOUT_SL_MASK >> | ||
135 | CLKOUT_SL_SHIFT) - | ||
136 | ((cdv * clkext - 1) / 8000000); | ||
137 | if (slew < 0) | ||
138 | slew = 0; | ||
139 | } | ||
140 | priv->clkout |= (slew << CLKOUT_SL_SHIFT) & | ||
141 | CLKOUT_SL_MASK; | ||
142 | } else { | ||
143 | dev_dbg(&pdev->dev, "invalid clock-out-frequency\n"); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int __devinit cc770_get_platform_data(struct platform_device *pdev, | ||
151 | struct cc770_priv *priv) | ||
152 | { | ||
153 | |||
154 | struct cc770_platform_data *pdata = pdev->dev.platform_data; | ||
155 | |||
156 | priv->can.clock.freq = pdata->osc_freq; | ||
157 | if (priv->cpu_interface | CPUIF_DSC) | ||
158 | priv->can.clock.freq /= 2; | ||
159 | priv->clkout = pdata->cor; | ||
160 | priv->bus_config = pdata->bcr; | ||
161 | priv->cpu_interface = pdata->cir; | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int __devinit cc770_platform_probe(struct platform_device *pdev) | ||
167 | { | ||
168 | struct net_device *dev; | ||
169 | struct cc770_priv *priv; | ||
170 | struct resource *mem; | ||
171 | resource_size_t mem_size; | ||
172 | void __iomem *base; | ||
173 | int err, irq; | ||
174 | |||
175 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
176 | irq = platform_get_irq(pdev, 0); | ||
177 | if (!mem || irq <= 0) | ||
178 | return -ENODEV; | ||
179 | |||
180 | mem_size = resource_size(mem); | ||
181 | if (!request_mem_region(mem->start, mem_size, pdev->name)) | ||
182 | return -EBUSY; | ||
183 | |||
184 | base = ioremap(mem->start, mem_size); | ||
185 | if (!base) { | ||
186 | err = -ENOMEM; | ||
187 | goto exit_release_mem; | ||
188 | } | ||
189 | |||
190 | dev = alloc_cc770dev(0); | ||
191 | if (!dev) { | ||
192 | err = -ENOMEM; | ||
193 | goto exit_unmap_mem; | ||
194 | } | ||
195 | |||
196 | dev->irq = irq; | ||
197 | priv = netdev_priv(dev); | ||
198 | priv->read_reg = cc770_platform_read_reg; | ||
199 | priv->write_reg = cc770_platform_write_reg; | ||
200 | priv->irq_flags = IRQF_SHARED; | ||
201 | priv->reg_base = base; | ||
202 | |||
203 | if (pdev->dev.of_node) | ||
204 | err = cc770_get_of_node_data(pdev, priv); | ||
205 | else if (pdev->dev.platform_data) | ||
206 | err = cc770_get_platform_data(pdev, priv); | ||
207 | else | ||
208 | err = -ENODEV; | ||
209 | if (err) | ||
210 | goto exit_free_cc770; | ||
211 | |||
212 | dev_dbg(&pdev->dev, | ||
213 | "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x " | ||
214 | "bus_config=0x%02x clkout=0x%02x\n", | ||
215 | priv->reg_base, dev->irq, priv->can.clock.freq, | ||
216 | priv->cpu_interface, priv->bus_config, priv->clkout); | ||
217 | |||
218 | dev_set_drvdata(&pdev->dev, dev); | ||
219 | SET_NETDEV_DEV(dev, &pdev->dev); | ||
220 | |||
221 | err = register_cc770dev(dev); | ||
222 | if (err) { | ||
223 | dev_err(&pdev->dev, | ||
224 | "couldn't register CC700 device (err=%d)\n", err); | ||
225 | goto exit_free_cc770; | ||
226 | } | ||
227 | |||
228 | return 0; | ||
229 | |||
230 | exit_free_cc770: | ||
231 | free_cc770dev(dev); | ||
232 | exit_unmap_mem: | ||
233 | iounmap(base); | ||
234 | exit_release_mem: | ||
235 | release_mem_region(mem->start, mem_size); | ||
236 | |||
237 | return err; | ||
238 | } | ||
239 | |||
240 | static int __devexit cc770_platform_remove(struct platform_device *pdev) | ||
241 | { | ||
242 | struct net_device *dev = dev_get_drvdata(&pdev->dev); | ||
243 | struct cc770_priv *priv = netdev_priv(dev); | ||
244 | struct resource *mem; | ||
245 | |||
246 | unregister_cc770dev(dev); | ||
247 | iounmap(priv->reg_base); | ||
248 | free_cc770dev(dev); | ||
249 | |||
250 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
251 | release_mem_region(mem->start, resource_size(mem)); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static struct of_device_id __devinitdata cc770_platform_table[] = { | ||
257 | {.compatible = "bosch,cc770"}, /* CC770 from Bosch */ | ||
258 | {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ | ||
259 | {}, | ||
260 | }; | ||
261 | |||
262 | static struct platform_driver cc770_platform_driver = { | ||
263 | .driver = { | ||
264 | .name = DRV_NAME, | ||
265 | .owner = THIS_MODULE, | ||
266 | .of_match_table = cc770_platform_table, | ||
267 | }, | ||
268 | .probe = cc770_platform_probe, | ||
269 | .remove = __devexit_p(cc770_platform_remove), | ||
270 | }; | ||
271 | |||
272 | module_platform_driver(cc770_platform_driver); | ||