Commit | Line | Data |
---|---|---|
881ff67a BS |
1 | /* |
2 | * Platform CAN bus driver for Bosch C_CAN controller | |
3 | * | |
4 | * Copyright (C) 2010 ST Microelectronics | |
5 | * Bhupesh Sharma <bhupesh.sharma@st.com> | |
6 | * | |
7 | * Borrowed heavily from the C_CAN driver originally written by: | |
8 | * Copyright (C) 2007 | |
9 | * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de> | |
10 | * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch> | |
11 | * | |
12 | * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B. | |
13 | * Bosch C_CAN user manual can be obtained from: | |
14 | * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/ | |
15 | * users_manual_c_can.pdf | |
16 | * | |
17 | * This file is licensed under the terms of the GNU General Public | |
18 | * License version 2. This program is licensed "as is" without any | |
19 | * warranty of any kind, whether express or implied. | |
20 | */ | |
21 | ||
22 | #include <linux/kernel.h> | |
881ff67a BS |
23 | #include <linux/module.h> |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/netdevice.h> | |
27 | #include <linux/if_arp.h> | |
28 | #include <linux/if_ether.h> | |
29 | #include <linux/list.h> | |
881ff67a BS |
30 | #include <linux/io.h> |
31 | #include <linux/platform_device.h> | |
32 | #include <linux/clk.h> | |
2469627d AC |
33 | #include <linux/of.h> |
34 | #include <linux/of_device.h> | |
006cd138 | 35 | #include <linux/pinctrl/consumer.h> |
881ff67a BS |
36 | |
37 | #include <linux/can/dev.h> | |
38 | ||
39 | #include "c_can.h" | |
40 | ||
52cde85a AC |
41 | #define CAN_RAMINIT_START_MASK(i) (1 << (i)) |
42 | ||
881ff67a BS |
43 | /* |
44 | * 16-bit c_can registers can be arranged differently in the memory | |
45 | * architecture of different implementations. For example: 16-bit | |
46 | * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. | |
47 | * Handle the same by providing a common read/write interface. | |
48 | */ | |
49 | static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv, | |
33f81009 | 50 | enum reg index) |
881ff67a | 51 | { |
33f81009 | 52 | return readw(priv->base + priv->regs[index]); |
881ff67a BS |
53 | } |
54 | ||
55 | static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv, | |
33f81009 | 56 | enum reg index, u16 val) |
881ff67a | 57 | { |
33f81009 | 58 | writew(val, priv->base + priv->regs[index]); |
881ff67a BS |
59 | } |
60 | ||
61 | static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv, | |
33f81009 | 62 | enum reg index) |
881ff67a | 63 | { |
33f81009 | 64 | return readw(priv->base + 2 * priv->regs[index]); |
881ff67a BS |
65 | } |
66 | ||
67 | static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, | |
33f81009 | 68 | enum reg index, u16 val) |
881ff67a | 69 | { |
33f81009 | 70 | writew(val, priv->base + 2 * priv->regs[index]); |
881ff67a BS |
71 | } |
72 | ||
52cde85a AC |
73 | static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) |
74 | { | |
75 | u32 val; | |
76 | ||
77 | val = readl(priv->raminit_ctrlreg); | |
78 | if (enable) | |
79 | val |= CAN_RAMINIT_START_MASK(priv->instance); | |
80 | else | |
81 | val &= ~CAN_RAMINIT_START_MASK(priv->instance); | |
82 | writel(val, priv->raminit_ctrlreg); | |
83 | } | |
84 | ||
2469627d AC |
85 | static struct platform_device_id c_can_id_table[] = { |
86 | [BOSCH_C_CAN_PLATFORM] = { | |
87 | .name = KBUILD_MODNAME, | |
88 | .driver_data = BOSCH_C_CAN, | |
89 | }, | |
90 | [BOSCH_C_CAN] = { | |
91 | .name = "c_can", | |
92 | .driver_data = BOSCH_C_CAN, | |
93 | }, | |
94 | [BOSCH_D_CAN] = { | |
95 | .name = "d_can", | |
96 | .driver_data = BOSCH_D_CAN, | |
97 | }, { | |
98 | } | |
99 | }; | |
100 | ||
101 | static const struct of_device_id c_can_of_table[] = { | |
102 | { .compatible = "bosch,c_can", .data = &c_can_id_table[BOSCH_C_CAN] }, | |
103 | { .compatible = "bosch,d_can", .data = &c_can_id_table[BOSCH_D_CAN] }, | |
104 | { /* sentinel */ }, | |
105 | }; | |
106 | ||
881ff67a BS |
107 | static int __devinit c_can_plat_probe(struct platform_device *pdev) |
108 | { | |
109 | int ret; | |
110 | void __iomem *addr; | |
111 | struct net_device *dev; | |
112 | struct c_can_priv *priv; | |
2469627d | 113 | const struct of_device_id *match; |
69927fcc | 114 | const struct platform_device_id *id; |
006cd138 | 115 | struct pinctrl *pinctrl; |
52cde85a | 116 | struct resource *mem, *res; |
b0052b08 | 117 | int irq; |
881ff67a BS |
118 | struct clk *clk; |
119 | ||
2469627d AC |
120 | if (pdev->dev.of_node) { |
121 | match = of_match_device(c_can_of_table, &pdev->dev); | |
122 | if (!match) { | |
123 | dev_err(&pdev->dev, "Failed to find matching dt id\n"); | |
124 | ret = -EINVAL; | |
125 | goto exit; | |
126 | } | |
127 | id = match->data; | |
128 | } else { | |
129 | id = platform_get_device_id(pdev); | |
130 | } | |
131 | ||
006cd138 AC |
132 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); |
133 | if (IS_ERR(pinctrl)) | |
134 | dev_warn(&pdev->dev, | |
135 | "failed to configure pins from driver\n"); | |
136 | ||
881ff67a BS |
137 | /* get the appropriate clk */ |
138 | clk = clk_get(&pdev->dev, NULL); | |
139 | if (IS_ERR(clk)) { | |
140 | dev_err(&pdev->dev, "no clock defined\n"); | |
141 | ret = -ENODEV; | |
142 | goto exit; | |
143 | } | |
881ff67a BS |
144 | |
145 | /* get the platform data */ | |
146 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
b0052b08 MKB |
147 | irq = platform_get_irq(pdev, 0); |
148 | if (!mem || irq <= 0) { | |
881ff67a BS |
149 | ret = -ENODEV; |
150 | goto exit_free_clk; | |
151 | } | |
152 | ||
153 | if (!request_mem_region(mem->start, resource_size(mem), | |
154 | KBUILD_MODNAME)) { | |
155 | dev_err(&pdev->dev, "resource unavailable\n"); | |
156 | ret = -ENODEV; | |
157 | goto exit_free_clk; | |
158 | } | |
159 | ||
160 | addr = ioremap(mem->start, resource_size(mem)); | |
161 | if (!addr) { | |
162 | dev_err(&pdev->dev, "failed to map can port\n"); | |
163 | ret = -ENOMEM; | |
164 | goto exit_release_mem; | |
165 | } | |
166 | ||
167 | /* allocate the c_can device */ | |
168 | dev = alloc_c_can_dev(); | |
169 | if (!dev) { | |
170 | ret = -ENOMEM; | |
171 | goto exit_iounmap; | |
172 | } | |
173 | ||
174 | priv = netdev_priv(dev); | |
69927fcc | 175 | switch (id->driver_data) { |
f27b1db9 | 176 | case BOSCH_C_CAN: |
69927fcc AC |
177 | priv->regs = reg_map_c_can; |
178 | switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) { | |
179 | case IORESOURCE_MEM_32BIT: | |
180 | priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; | |
181 | priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; | |
182 | break; | |
183 | case IORESOURCE_MEM_16BIT: | |
184 | default: | |
185 | priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; | |
186 | priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; | |
187 | break; | |
188 | } | |
189 | break; | |
f27b1db9 | 190 | case BOSCH_D_CAN: |
69927fcc AC |
191 | priv->regs = reg_map_d_can; |
192 | priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; | |
193 | priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; | |
194 | priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; | |
52cde85a AC |
195 | |
196 | if (pdev->dev.of_node) | |
197 | priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can"); | |
198 | else | |
199 | priv->instance = pdev->id; | |
200 | ||
201 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
202 | priv->raminit_ctrlreg = devm_request_and_ioremap(&pdev->dev, res); | |
203 | if (!priv->raminit_ctrlreg || priv->instance < 0) | |
204 | dev_info(&pdev->dev, "control memory is not used for raminit\n"); | |
205 | else | |
206 | priv->raminit = c_can_hw_raminit; | |
69927fcc AC |
207 | break; |
208 | default: | |
209 | ret = -EINVAL; | |
210 | goto exit_free_device; | |
211 | } | |
881ff67a | 212 | |
b0052b08 | 213 | dev->irq = irq; |
33f81009 | 214 | priv->base = addr; |
4cdd34b2 | 215 | priv->device = &pdev->dev; |
881ff67a BS |
216 | priv->can.clock.freq = clk_get_rate(clk); |
217 | priv->priv = clk; | |
82120032 | 218 | priv->type = id->driver_data; |
881ff67a | 219 | |
881ff67a BS |
220 | platform_set_drvdata(pdev, dev); |
221 | SET_NETDEV_DEV(dev, &pdev->dev); | |
222 | ||
223 | ret = register_c_can_dev(dev); | |
224 | if (ret) { | |
225 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", | |
226 | KBUILD_MODNAME, ret); | |
227 | goto exit_free_device; | |
228 | } | |
229 | ||
230 | dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", | |
33f81009 | 231 | KBUILD_MODNAME, priv->base, dev->irq); |
881ff67a BS |
232 | return 0; |
233 | ||
234 | exit_free_device: | |
235 | platform_set_drvdata(pdev, NULL); | |
236 | free_c_can_dev(dev); | |
237 | exit_iounmap: | |
238 | iounmap(addr); | |
239 | exit_release_mem: | |
240 | release_mem_region(mem->start, resource_size(mem)); | |
241 | exit_free_clk: | |
881ff67a BS |
242 | clk_put(clk); |
243 | exit: | |
881ff67a BS |
244 | dev_err(&pdev->dev, "probe failed\n"); |
245 | ||
246 | return ret; | |
247 | } | |
248 | ||
249 | static int __devexit c_can_plat_remove(struct platform_device *pdev) | |
250 | { | |
251 | struct net_device *dev = platform_get_drvdata(pdev); | |
252 | struct c_can_priv *priv = netdev_priv(dev); | |
253 | struct resource *mem; | |
254 | ||
255 | unregister_c_can_dev(dev); | |
256 | platform_set_drvdata(pdev, NULL); | |
257 | ||
258 | free_c_can_dev(dev); | |
33f81009 | 259 | iounmap(priv->base); |
881ff67a BS |
260 | |
261 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
262 | release_mem_region(mem->start, resource_size(mem)); | |
263 | ||
881ff67a | 264 | clk_put(priv->priv); |
881ff67a BS |
265 | |
266 | return 0; | |
267 | } | |
268 | ||
82120032 AC |
269 | #ifdef CONFIG_PM |
270 | static int c_can_suspend(struct platform_device *pdev, pm_message_t state) | |
271 | { | |
272 | int ret; | |
273 | struct net_device *ndev = platform_get_drvdata(pdev); | |
274 | struct c_can_priv *priv = netdev_priv(ndev); | |
275 | ||
276 | if (priv->type != BOSCH_D_CAN) { | |
277 | dev_warn(&pdev->dev, "Not supported\n"); | |
278 | return 0; | |
279 | } | |
280 | ||
281 | if (netif_running(ndev)) { | |
282 | netif_stop_queue(ndev); | |
283 | netif_device_detach(ndev); | |
284 | } | |
285 | ||
286 | ret = c_can_power_down(ndev); | |
287 | if (ret) { | |
288 | netdev_err(ndev, "failed to enter power down mode\n"); | |
289 | return ret; | |
290 | } | |
291 | ||
292 | priv->can.state = CAN_STATE_SLEEPING; | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static int c_can_resume(struct platform_device *pdev) | |
298 | { | |
299 | int ret; | |
300 | struct net_device *ndev = platform_get_drvdata(pdev); | |
301 | struct c_can_priv *priv = netdev_priv(ndev); | |
302 | ||
303 | if (priv->type != BOSCH_D_CAN) { | |
304 | dev_warn(&pdev->dev, "Not supported\n"); | |
305 | return 0; | |
306 | } | |
307 | ||
308 | ret = c_can_power_up(ndev); | |
309 | if (ret) { | |
310 | netdev_err(ndev, "Still in power down mode\n"); | |
311 | return ret; | |
312 | } | |
313 | ||
314 | priv->can.state = CAN_STATE_ERROR_ACTIVE; | |
315 | ||
316 | if (netif_running(ndev)) { | |
317 | netif_device_attach(ndev); | |
318 | netif_start_queue(ndev); | |
319 | } | |
320 | ||
321 | return 0; | |
322 | } | |
323 | #else | |
324 | #define c_can_suspend NULL | |
325 | #define c_can_resume NULL | |
326 | #endif | |
327 | ||
881ff67a BS |
328 | static struct platform_driver c_can_plat_driver = { |
329 | .driver = { | |
330 | .name = KBUILD_MODNAME, | |
331 | .owner = THIS_MODULE, | |
2469627d | 332 | .of_match_table = of_match_ptr(c_can_of_table), |
881ff67a BS |
333 | }, |
334 | .probe = c_can_plat_probe, | |
335 | .remove = __devexit_p(c_can_plat_remove), | |
82120032 AC |
336 | .suspend = c_can_suspend, |
337 | .resume = c_can_resume, | |
69927fcc | 338 | .id_table = c_can_id_table, |
881ff67a BS |
339 | }; |
340 | ||
871d3372 | 341 | module_platform_driver(c_can_plat_driver); |
881ff67a BS |
342 | |
343 | MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>"); | |
344 | MODULE_LICENSE("GPL v2"); | |
345 | MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller"); |