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