diff options
author | Federico Vaga <federico.vaga@gmail.com> | 2012-06-14 07:43:42 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2012-06-19 15:22:28 -0400 |
commit | 5b92da0443c2585e31b64e86c2e1b8e22845d4bb (patch) | |
tree | 9ceb252a75f7fe6dc1f1f3a834fdf572a515c6a5 | |
parent | b31525d16b50fe0eb33545afbc0be1a03f2896e3 (diff) |
c_can_pci: generic module for C_CAN/D_CAN on PCI
Signed-off-by: Federico Vaga <federico.vaga@gmail.com>
Acked-by: Giancarlo Asnaghi <giancarlo.asnaghi@st.com>
Cc: Alan Cox <alan@linux.intel.com>
Acked-by: Wolfgang Grandegger <wg@grandegger.com>
Acked-by: Bhupesh Sharma <bhupesh.sharma@st.com>
[mkl: fix call to pci_iounmap]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r-- | drivers/net/can/c_can/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/can/c_can/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/c_can/c_can_pci.c | 236 |
3 files changed, 244 insertions, 0 deletions
diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index 25d371cf98dd..3b83bafcd947 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig | |||
@@ -13,4 +13,11 @@ config CAN_C_CAN_PLATFORM | |||
13 | boards from ST Microelectronics (http://www.st.com) like the | 13 | boards from ST Microelectronics (http://www.st.com) like the |
14 | SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com) | 14 | SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com) |
15 | boards like am335x, dm814x, dm813x and dm811x. | 15 | boards like am335x, dm814x, dm813x and dm811x. |
16 | |||
17 | config CAN_C_CAN_PCI | ||
18 | tristate "Generic PCI Bus based C_CAN/D_CAN driver" | ||
19 | depends on PCI | ||
20 | ---help--- | ||
21 | This driver adds support for the C_CAN/D_CAN chips connected | ||
22 | to the PCI bus. | ||
16 | endif | 23 | endif |
diff --git a/drivers/net/can/c_can/Makefile b/drivers/net/can/c_can/Makefile index 9273f6d5c4b7..ad1cc842170a 100644 --- a/drivers/net/can/c_can/Makefile +++ b/drivers/net/can/c_can/Makefile | |||
@@ -4,5 +4,6 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_CAN_C_CAN) += c_can.o | 5 | obj-$(CONFIG_CAN_C_CAN) += c_can.o |
6 | obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o | 6 | obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o |
7 | obj-$(CONFIG_CAN_C_CAN_PCI) += c_can_pci.o | ||
7 | 8 | ||
8 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG | 9 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG |
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c new file mode 100644 index 000000000000..914aecfa09a9 --- /dev/null +++ b/drivers/net/can/c_can/c_can_pci.c | |||
@@ -0,0 +1,236 @@ | |||
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 | |||
23 | enum c_can_pci_reg_align { | ||
24 | C_CAN_REG_ALIGN_16, | ||
25 | C_CAN_REG_ALIGN_32, | ||
26 | }; | ||
27 | |||
28 | struct 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 | */ | ||
43 | static 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 | |||
49 | static 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 | |||
55 | static 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 | |||
61 | static 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 | |||
67 | static 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 | |||
171 | out_free_clock: | ||
172 | if (priv->priv) | ||
173 | clk_put(priv->priv); | ||
174 | out_free_c_can: | ||
175 | pci_set_drvdata(pdev, NULL); | ||
176 | free_c_can_dev(dev); | ||
177 | out_iounmap: | ||
178 | pci_iounmap(pdev, addr); | ||
179 | out_release_regions: | ||
180 | pci_disable_msi(pdev); | ||
181 | pci_clear_master(pdev); | ||
182 | pci_release_regions(pdev); | ||
183 | out_disable_device: | ||
184 | pci_disable_device(pdev); | ||
185 | out: | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static 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 | |||
209 | static 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 | } | ||
219 | static 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 | }; | ||
224 | static 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 | |||
231 | module_pci_driver(c_can_pci_driver); | ||
232 | |||
233 | MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>"); | ||
234 | MODULE_LICENSE("GPL v2"); | ||
235 | MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller"); | ||
236 | MODULE_DEVICE_TABLE(pci, c_can_pci_tbl); | ||