c_can_platform.c revision 2b1463ddde35a720de2fb4013057396e7f8011f0
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	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	}
86
87	/* get the platform data */
88	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
89	irq = platform_get_irq(pdev, 0);
90	if (!mem || irq <= 0) {
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);
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	}
143
144	dev->irq = irq;
145	priv->base = addr;
146	priv->can.clock.freq = clk_get_rate(clk);
147	priv->priv = clk;
148
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",
160		 KBUILD_MODNAME, priv->base, dev->irq);
161	return 0;
162
163exit_free_device:
164	platform_set_drvdata(pdev, NULL);
165	free_c_can_dev(dev);
166exit_iounmap:
167	iounmap(addr);
168exit_release_mem:
169	release_mem_region(mem->start, resource_size(mem));
170exit_free_clk:
171	clk_put(clk);
172exit:
173	dev_err(&pdev->dev, "probe failed\n");
174
175	return ret;
176}
177
178static 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);
188	iounmap(priv->base);
189
190	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191	release_mem_region(mem->start, resource_size(mem));
192
193	clk_put(priv->priv);
194
195	return 0;
196}
197
198static 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
212static 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),
219	.id_table = c_can_id_table,
220};
221
222module_platform_driver(c_can_plat_driver);
223
224MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>");
225MODULE_LICENSE("GPL v2");
226MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller");
227