c_can_pci.c revision 5b92da0443c2585e31b64e86c2e1b8e22845d4bb
1/*
2 * PCI bus driver for Bosch C_CAN/D_CAN controller
3 *
4 * Copyright (C) 2012 Federico Vaga <federico.vaga@gmail.com>
5 *
6 * Borrowed from c_can_platform.c
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/netdevice.h>
16#include <linux/clk.h>
17#include <linux/pci.h>
18
19#include <linux/can/dev.h>
20
21#include "c_can.h"
22
23enum c_can_pci_reg_align {
24	C_CAN_REG_ALIGN_16,
25	C_CAN_REG_ALIGN_32,
26};
27
28struct c_can_pci_data {
29	/* Specify if is C_CAN or D_CAN */
30	enum c_can_dev_id type;
31	/* Set the register alignment in the memory */
32	enum c_can_pci_reg_align reg_align;
33	/* Set the frequency if clk is not usable */
34	unsigned int freq;
35};
36
37/*
38 * 16-bit c_can registers can be arranged differently in the memory
39 * architecture of different implementations. For example: 16-bit
40 * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
41 * Handle the same by providing a common read/write interface.
42 */
43static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv,
44						enum reg index)
45{
46	return readw(priv->base + priv->regs[index]);
47}
48
49static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv,
50						enum reg index, u16 val)
51{
52	writew(val, priv->base + priv->regs[index]);
53}
54
55static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv,
56						enum reg index)
57{
58	return readw(priv->base + 2 * priv->regs[index]);
59}
60
61static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
62						enum reg index, u16 val)
63{
64	writew(val, priv->base + 2 * priv->regs[index]);
65}
66
67static int __devinit c_can_pci_probe(struct pci_dev *pdev,
68				     const struct pci_device_id *ent)
69{
70	struct c_can_pci_data *c_can_pci_data = (void *)ent->driver_data;
71	struct c_can_priv *priv;
72	struct net_device *dev;
73	void __iomem *addr;
74	struct clk *clk;
75	int ret;
76
77	ret = pci_enable_device(pdev);
78	if (ret) {
79		dev_err(&pdev->dev, "pci_enable_device FAILED\n");
80		goto out;
81	}
82
83	ret = pci_request_regions(pdev, KBUILD_MODNAME);
84	if (ret) {
85		dev_err(&pdev->dev, "pci_request_regions FAILED\n");
86		goto out_disable_device;
87	}
88
89	pci_set_master(pdev);
90	pci_enable_msi(pdev);
91
92	addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
93	if (!addr) {
94		dev_err(&pdev->dev,
95			"device has no PCI memory resources, "
96			"failing adapter\n");
97		ret = -ENOMEM;
98		goto out_release_regions;
99	}
100
101	/* allocate the c_can device */
102	dev = alloc_c_can_dev();
103	if (!dev) {
104		ret = -ENOMEM;
105		goto out_iounmap;
106	}
107
108	priv = netdev_priv(dev);
109	pci_set_drvdata(pdev, dev);
110	SET_NETDEV_DEV(dev, &pdev->dev);
111
112	dev->irq = pdev->irq;
113	priv->base = addr;
114
115	if (!c_can_pci_data->freq) {
116		/* get the appropriate clk */
117		clk = clk_get(&pdev->dev, NULL);
118		if (IS_ERR(clk)) {
119			dev_err(&pdev->dev, "no clock defined\n");
120			ret = -ENODEV;
121			goto out_free_c_can;
122		}
123		priv->can.clock.freq = clk_get_rate(clk);
124		priv->priv = clk;
125	} else {
126		priv->can.clock.freq = c_can_pci_data->freq;
127		priv->priv = NULL;
128	}
129
130	/* Configure CAN type */
131	switch (c_can_pci_data->type) {
132	case C_CAN_DEVTYPE:
133		priv->regs = reg_map_c_can;
134		break;
135	case D_CAN_DEVTYPE:
136		priv->regs = reg_map_d_can;
137		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
138		break;
139	default:
140		ret = -EINVAL;
141		goto out_free_clock;
142	}
143
144	/* Configure access to registers */
145	switch (c_can_pci_data->reg_align) {
146	case C_CAN_REG_ALIGN_32:
147		priv->read_reg = c_can_pci_read_reg_aligned_to_32bit;
148		priv->write_reg = c_can_pci_write_reg_aligned_to_32bit;
149		break;
150	case C_CAN_REG_ALIGN_16:
151		priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
152		priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
153		break;
154	default:
155		ret = -EINVAL;
156		goto out_free_clock;
157	}
158
159	ret = register_c_can_dev(dev);
160	if (ret) {
161		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
162			KBUILD_MODNAME, ret);
163		goto out_free_clock;
164	}
165
166	dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
167		 KBUILD_MODNAME, priv->regs, dev->irq);
168
169	return 0;
170
171out_free_clock:
172	if (priv->priv)
173		clk_put(priv->priv);
174out_free_c_can:
175	pci_set_drvdata(pdev, NULL);
176	free_c_can_dev(dev);
177out_iounmap:
178	pci_iounmap(pdev, addr);
179out_release_regions:
180	pci_disable_msi(pdev);
181	pci_clear_master(pdev);
182	pci_release_regions(pdev);
183out_disable_device:
184	pci_disable_device(pdev);
185out:
186	return ret;
187}
188
189static void __devexit c_can_pci_remove(struct pci_dev *pdev)
190{
191	struct net_device *dev = pci_get_drvdata(pdev);
192	struct c_can_priv *priv = netdev_priv(dev);
193
194	unregister_c_can_dev(dev);
195
196	if (priv->priv)
197		clk_put(priv->priv);
198
199	pci_set_drvdata(pdev, NULL);
200	free_c_can_dev(dev);
201
202	pci_iounmap(pdev, priv->base);
203	pci_disable_msi(pdev);
204	pci_clear_master(pdev);
205	pci_release_regions(pdev);
206	pci_disable_device(pdev);
207}
208
209static struct c_can_pci_data c_can_sta2x11= {
210	.type = C_CAN_DEVTYPE,
211	.reg_align = C_CAN_REG_ALIGN_32,
212	.freq = 52000000, /* 52 Mhz */
213};
214
215#define C_CAN_ID(_vend, _dev, _driverdata) {		\
216	PCI_DEVICE(_vend, _dev),			\
217	.driver_data = (unsigned long)&_driverdata,	\
218}
219static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {
220	C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
221		 c_can_sta2x11),
222	{},
223};
224static struct pci_driver c_can_pci_driver = {
225	.name = KBUILD_MODNAME,
226	.id_table = c_can_pci_tbl,
227	.probe = c_can_pci_probe,
228	.remove = __devexit_p(c_can_pci_remove),
229};
230
231module_pci_driver(c_can_pci_driver);
232
233MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>");
234MODULE_LICENSE("GPL v2");
235MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller");
236MODULE_DEVICE_TABLE(pci, c_can_pci_tbl);
237