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> | |
33 | ||
34 | #include <linux/can/dev.h> | |
35 | ||
36 | #include "c_can.h" | |
37 | ||
38 | /* | |
39 | * 16-bit c_can registers can be arranged differently in the memory | |
40 | * architecture of different implementations. For example: 16-bit | |
41 | * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. | |
42 | * Handle the same by providing a common read/write interface. | |
43 | */ | |
44 | static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv, | |
33f81009 | 45 | enum reg index) |
881ff67a | 46 | { |
33f81009 | 47 | return readw(priv->base + priv->regs[index]); |
881ff67a BS |
48 | } |
49 | ||
50 | static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv, | |
33f81009 | 51 | enum reg index, u16 val) |
881ff67a | 52 | { |
33f81009 | 53 | writew(val, priv->base + priv->regs[index]); |
881ff67a BS |
54 | } |
55 | ||
56 | static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv, | |
33f81009 | 57 | enum reg index) |
881ff67a | 58 | { |
33f81009 | 59 | return readw(priv->base + 2 * priv->regs[index]); |
881ff67a BS |
60 | } |
61 | ||
62 | static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, | |
33f81009 | 63 | enum reg index, u16 val) |
881ff67a | 64 | { |
33f81009 | 65 | writew(val, priv->base + 2 * priv->regs[index]); |
881ff67a BS |
66 | } |
67 | ||
68 | static int __devinit c_can_plat_probe(struct platform_device *pdev) | |
69 | { | |
70 | int ret; | |
71 | void __iomem *addr; | |
72 | struct net_device *dev; | |
73 | struct c_can_priv *priv; | |
69927fcc | 74 | const struct platform_device_id *id; |
b0052b08 MKB |
75 | struct resource *mem; |
76 | int irq; | |
881ff67a BS |
77 | struct clk *clk; |
78 | ||
79 | /* get the appropriate clk */ | |
80 | clk = clk_get(&pdev->dev, NULL); | |
81 | if (IS_ERR(clk)) { | |
82 | dev_err(&pdev->dev, "no clock defined\n"); | |
83 | ret = -ENODEV; | |
84 | goto exit; | |
85 | } | |
881ff67a BS |
86 | |
87 | /* get the platform data */ | |
88 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
b0052b08 MKB |
89 | irq = platform_get_irq(pdev, 0); |
90 | if (!mem || irq <= 0) { | |
881ff67a BS |
91 | ret = -ENODEV; |
92 | goto exit_free_clk; | |
93 | } | |
94 | ||
95 | if (!request_mem_region(mem->start, resource_size(mem), | |
96 | KBUILD_MODNAME)) { | |
97 | dev_err(&pdev->dev, "resource unavailable\n"); | |
98 | ret = -ENODEV; | |
99 | goto exit_free_clk; | |
100 | } | |
101 | ||
102 | addr = ioremap(mem->start, resource_size(mem)); | |
103 | if (!addr) { | |
104 | dev_err(&pdev->dev, "failed to map can port\n"); | |
105 | ret = -ENOMEM; | |
106 | goto exit_release_mem; | |
107 | } | |
108 | ||
109 | /* allocate the c_can device */ | |
110 | dev = alloc_c_can_dev(); | |
111 | if (!dev) { | |
112 | ret = -ENOMEM; | |
113 | goto exit_iounmap; | |
114 | } | |
115 | ||
116 | priv = netdev_priv(dev); | |
69927fcc AC |
117 | id = platform_get_device_id(pdev); |
118 | switch (id->driver_data) { | |
119 | case C_CAN_DEVTYPE: | |
120 | priv->regs = reg_map_c_can; | |
121 | switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) { | |
122 | case IORESOURCE_MEM_32BIT: | |
123 | priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; | |
124 | priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; | |
125 | break; | |
126 | case IORESOURCE_MEM_16BIT: | |
127 | default: | |
128 | priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; | |
129 | priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; | |
130 | break; | |
131 | } | |
132 | break; | |
133 | case D_CAN_DEVTYPE: | |
134 | priv->regs = reg_map_d_can; | |
135 | priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; | |
136 | priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; | |
137 | priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; | |
138 | break; | |
139 | default: | |
140 | ret = -EINVAL; | |
141 | goto exit_free_device; | |
142 | } | |
881ff67a | 143 | |
b0052b08 | 144 | dev->irq = irq; |
33f81009 | 145 | priv->base = addr; |
881ff67a BS |
146 | priv->can.clock.freq = clk_get_rate(clk); |
147 | priv->priv = clk; | |
881ff67a | 148 | |
881ff67a BS |
149 | platform_set_drvdata(pdev, dev); |
150 | SET_NETDEV_DEV(dev, &pdev->dev); | |
151 | ||
152 | ret = register_c_can_dev(dev); | |
153 | if (ret) { | |
154 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", | |
155 | KBUILD_MODNAME, ret); | |
156 | goto exit_free_device; | |
157 | } | |
158 | ||
159 | dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", | |
33f81009 | 160 | KBUILD_MODNAME, priv->base, dev->irq); |
881ff67a BS |
161 | return 0; |
162 | ||
163 | exit_free_device: | |
164 | platform_set_drvdata(pdev, NULL); | |
165 | free_c_can_dev(dev); | |
166 | exit_iounmap: | |
167 | iounmap(addr); | |
168 | exit_release_mem: | |
169 | release_mem_region(mem->start, resource_size(mem)); | |
170 | exit_free_clk: | |
881ff67a BS |
171 | clk_put(clk); |
172 | exit: | |
881ff67a BS |
173 | dev_err(&pdev->dev, "probe failed\n"); |
174 | ||
175 | return ret; | |
176 | } | |
177 | ||
178 | static int __devexit c_can_plat_remove(struct platform_device *pdev) | |
179 | { | |
180 | struct net_device *dev = platform_get_drvdata(pdev); | |
181 | struct c_can_priv *priv = netdev_priv(dev); | |
182 | struct resource *mem; | |
183 | ||
184 | unregister_c_can_dev(dev); | |
185 | platform_set_drvdata(pdev, NULL); | |
186 | ||
187 | free_c_can_dev(dev); | |
33f81009 | 188 | iounmap(priv->base); |
881ff67a BS |
189 | |
190 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
191 | release_mem_region(mem->start, resource_size(mem)); | |
192 | ||
881ff67a | 193 | clk_put(priv->priv); |
881ff67a BS |
194 | |
195 | return 0; | |
196 | } | |
197 | ||
69927fcc AC |
198 | static const struct platform_device_id c_can_id_table[] = { |
199 | { | |
200 | .name = KBUILD_MODNAME, | |
201 | .driver_data = C_CAN_DEVTYPE, | |
202 | }, { | |
203 | .name = "c_can", | |
204 | .driver_data = C_CAN_DEVTYPE, | |
205 | }, { | |
206 | .name = "d_can", | |
207 | .driver_data = D_CAN_DEVTYPE, | |
208 | }, { | |
209 | } | |
210 | }; | |
211 | ||
881ff67a BS |
212 | static struct platform_driver c_can_plat_driver = { |
213 | .driver = { | |
214 | .name = KBUILD_MODNAME, | |
215 | .owner = THIS_MODULE, | |
216 | }, | |
217 | .probe = c_can_plat_probe, | |
218 | .remove = __devexit_p(c_can_plat_remove), | |
69927fcc | 219 | .id_table = c_can_id_table, |
881ff67a BS |
220 | }; |
221 | ||
871d3372 | 222 | module_platform_driver(c_can_plat_driver); |
881ff67a BS |
223 | |
224 | MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>"); | |
225 | MODULE_LICENSE("GPL v2"); | |
226 | MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller"); |