diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/can/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/sja1000/kvaser_pci.c | 411 |
3 files changed, 419 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index f1684cd03b3a..cfd6c5a285fa 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig | |||
@@ -58,6 +58,13 @@ config CAN_EMS_PCI | |||
58 | This driver is for the one or two channel CPC-PCI and CPC-PCIe | 58 | This driver is for the one or two channel CPC-PCI and CPC-PCIe |
59 | cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). | 59 | cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). |
60 | 60 | ||
61 | config CAN_KVASER_PCI | ||
62 | tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" | ||
63 | depends on PCI && CAN_SJA1000 | ||
64 | ---help--- | ||
65 | This driver is for the the PCIcanx and PCIcan cards (1, 2 or | ||
66 | 4 channel) from Kvaser (http://www.kvaser.com). | ||
67 | |||
61 | config CAN_DEBUG_DEVICES | 68 | config CAN_DEBUG_DEVICES |
62 | bool "CAN devices debugging messages" | 69 | bool "CAN devices debugging messages" |
63 | depends on CAN | 70 | depends on CAN |
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 7772dd23272d..d6c631f9e665 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile | |||
@@ -5,5 +5,6 @@ | |||
5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o | 5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o |
6 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o | 6 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o |
7 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o | 7 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o |
8 | obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o | ||
8 | 9 | ||
9 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG | 10 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG |
diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c new file mode 100644 index 000000000000..00830b358c4f --- /dev/null +++ b/drivers/net/can/sja1000/kvaser_pci.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se> | ||
3 | * | ||
4 | * Parts of this software are based on (derived) the following: | ||
5 | * | ||
6 | * - Kvaser linux driver, version 4.72 BETA | ||
7 | * Copyright (C) 2002-2007 KVASER AB | ||
8 | * | ||
9 | * - Lincan driver, version 0.3.3, OCERA project | ||
10 | * Copyright (C) 2004 Pavel Pisa | ||
11 | * Copyright (C) 2001 Arnaud Westenberg | ||
12 | * | ||
13 | * - Socketcan SJA1000 drivers | ||
14 | * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> | ||
15 | * Copyright (c) 2002-2007 Volkswagen Group Electronic Research | ||
16 | * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, | ||
17 | * 38106 Braunschweig, GERMANY | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the version 2 of the GNU General Public License | ||
21 | * as published by the Free Software Foundation | ||
22 | * | ||
23 | * This program is distributed in the hope that it will be useful, but | ||
24 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
26 | * General Public License for more details. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License | ||
29 | * along with this program; if not, write to the Free Software Foundation, | ||
30 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
31 | */ | ||
32 | |||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/netdevice.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <linux/can.h> | ||
40 | #include <linux/can/dev.h> | ||
41 | #include <linux/io.h> | ||
42 | |||
43 | #include "sja1000.h" | ||
44 | |||
45 | #define DRV_NAME "kvaser_pci" | ||
46 | |||
47 | MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>"); | ||
48 | MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards"); | ||
49 | MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card"); | ||
50 | MODULE_LICENSE("GPL v2"); | ||
51 | |||
52 | #define MAX_NO_OF_CHANNELS 4 /* max no of channels on a single card */ | ||
53 | |||
54 | struct kvaser_pci { | ||
55 | int channel; | ||
56 | struct pci_dev *pci_dev; | ||
57 | struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1]; | ||
58 | void __iomem *conf_addr; | ||
59 | void __iomem *res_addr; | ||
60 | int no_channels; | ||
61 | u8 xilinx_ver; | ||
62 | }; | ||
63 | |||
64 | #define KVASER_PCI_CAN_CLOCK (16000000 / 2) | ||
65 | |||
66 | /* | ||
67 | * The board configuration is probably following: | ||
68 | * RX1 is connected to ground. | ||
69 | * TX1 is not connected. | ||
70 | * CLKO is not connected. | ||
71 | * Setting the OCR register to 0xDA is a good idea. | ||
72 | * This means normal output mode , push-pull and the correct polarity. | ||
73 | */ | ||
74 | #define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) | ||
75 | |||
76 | /* | ||
77 | * In the CDR register, you should set CBP to 1. | ||
78 | * You will probably also want to set the clock divider value to 0 | ||
79 | * (meaning divide-by-2), the Pelican bit, and the clock-off bit | ||
80 | * (you will have no need for CLKOUT anyway). | ||
81 | */ | ||
82 | #define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) | ||
83 | |||
84 | /* | ||
85 | * These register values are valid for revision 14 of the Xilinx logic. | ||
86 | */ | ||
87 | #define XILINX_VERINT 7 /* Lower nibble simulate interrupts, | ||
88 | high nibble version number. */ | ||
89 | |||
90 | #define XILINX_PRESUMED_VERSION 14 | ||
91 | |||
92 | /* | ||
93 | * Important S5920 registers | ||
94 | */ | ||
95 | #define S5920_INTCSR 0x38 | ||
96 | #define S5920_PTCR 0x60 | ||
97 | #define INTCSR_ADDON_INTENABLE_M 0x2000 | ||
98 | |||
99 | |||
100 | #define KVASER_PCI_PORT_BYTES 0x20 | ||
101 | |||
102 | #define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */ | ||
103 | #define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */ | ||
104 | #define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */ | ||
105 | |||
106 | #define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */ | ||
107 | #define KVASER_PCI_DEVICE_ID1 0x8406 | ||
108 | |||
109 | #define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */ | ||
110 | #define KVASER_PCI_DEVICE_ID2 0x0008 | ||
111 | |||
112 | static struct pci_device_id kvaser_pci_tbl[] = { | ||
113 | {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,}, | ||
114 | {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,}, | ||
115 | { 0,} | ||
116 | }; | ||
117 | |||
118 | MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl); | ||
119 | |||
120 | static u8 kvaser_pci_read_reg(const struct net_device *dev, int port) | ||
121 | { | ||
122 | return ioread8((void __iomem *)(dev->base_addr + port)); | ||
123 | } | ||
124 | |||
125 | static void kvaser_pci_write_reg(const struct net_device *dev, int port, u8 val) | ||
126 | { | ||
127 | iowrite8(val, (void __iomem *)(dev->base_addr + port)); | ||
128 | } | ||
129 | |||
130 | static void kvaser_pci_disable_irq(struct net_device *dev) | ||
131 | { | ||
132 | struct sja1000_priv *priv = netdev_priv(dev); | ||
133 | struct kvaser_pci *board = priv->priv; | ||
134 | u32 intcsr; | ||
135 | |||
136 | /* Disable interrupts from card */ | ||
137 | intcsr = ioread32(board->conf_addr + S5920_INTCSR); | ||
138 | intcsr &= ~INTCSR_ADDON_INTENABLE_M; | ||
139 | iowrite32(intcsr, board->conf_addr + S5920_INTCSR); | ||
140 | } | ||
141 | |||
142 | static void kvaser_pci_enable_irq(struct net_device *dev) | ||
143 | { | ||
144 | struct sja1000_priv *priv = netdev_priv(dev); | ||
145 | struct kvaser_pci *board = priv->priv; | ||
146 | u32 tmp_en_io; | ||
147 | |||
148 | /* Enable interrupts from card */ | ||
149 | tmp_en_io = ioread32(board->conf_addr + S5920_INTCSR); | ||
150 | tmp_en_io |= INTCSR_ADDON_INTENABLE_M; | ||
151 | iowrite32(tmp_en_io, board->conf_addr + S5920_INTCSR); | ||
152 | } | ||
153 | |||
154 | static int number_of_sja1000_chip(void __iomem *base_addr) | ||
155 | { | ||
156 | u8 status; | ||
157 | int i; | ||
158 | |||
159 | for (i = 0; i < MAX_NO_OF_CHANNELS; i++) { | ||
160 | /* reset chip */ | ||
161 | iowrite8(MOD_RM, base_addr + | ||
162 | (i * KVASER_PCI_PORT_BYTES) + REG_MOD); | ||
163 | status = ioread8(base_addr + | ||
164 | (i * KVASER_PCI_PORT_BYTES) + REG_MOD); | ||
165 | /* check reset bit */ | ||
166 | if (!(status & MOD_RM)) | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | return i; | ||
171 | } | ||
172 | |||
173 | static void kvaser_pci_del_chan(struct net_device *dev) | ||
174 | { | ||
175 | struct sja1000_priv *priv; | ||
176 | struct kvaser_pci *board; | ||
177 | int i; | ||
178 | |||
179 | if (!dev) | ||
180 | return; | ||
181 | priv = netdev_priv(dev); | ||
182 | board = priv->priv; | ||
183 | if (!board) | ||
184 | return; | ||
185 | |||
186 | dev_info(&board->pci_dev->dev, "Removing device %s\n", | ||
187 | dev->name); | ||
188 | |||
189 | /* Disable PCI interrupts */ | ||
190 | kvaser_pci_disable_irq(dev); | ||
191 | |||
192 | for (i = 0; i < board->no_channels - 1; i++) { | ||
193 | if (board->slave_dev[i]) { | ||
194 | dev_info(&board->pci_dev->dev, "Removing device %s\n", | ||
195 | board->slave_dev[i]->name); | ||
196 | unregister_sja1000dev(board->slave_dev[i]); | ||
197 | free_sja1000dev(board->slave_dev[i]); | ||
198 | } | ||
199 | } | ||
200 | unregister_sja1000dev(dev); | ||
201 | |||
202 | pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr); | ||
203 | pci_iounmap(board->pci_dev, board->conf_addr); | ||
204 | pci_iounmap(board->pci_dev, board->res_addr); | ||
205 | |||
206 | free_sja1000dev(dev); | ||
207 | } | ||
208 | |||
209 | static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, | ||
210 | struct net_device **master_dev, | ||
211 | void __iomem *conf_addr, | ||
212 | void __iomem *res_addr, | ||
213 | unsigned long base_addr) | ||
214 | { | ||
215 | struct net_device *dev; | ||
216 | struct sja1000_priv *priv; | ||
217 | struct kvaser_pci *board; | ||
218 | int err, init_step; | ||
219 | |||
220 | dev = alloc_sja1000dev(sizeof(struct kvaser_pci)); | ||
221 | if (dev == NULL) | ||
222 | return -ENOMEM; | ||
223 | |||
224 | priv = netdev_priv(dev); | ||
225 | board = priv->priv; | ||
226 | |||
227 | board->pci_dev = pdev; | ||
228 | board->channel = channel; | ||
229 | |||
230 | /* S5920 */ | ||
231 | board->conf_addr = conf_addr; | ||
232 | |||
233 | /* XILINX board wide address */ | ||
234 | board->res_addr = res_addr; | ||
235 | |||
236 | if (channel == 0) { | ||
237 | board->xilinx_ver = | ||
238 | ioread8(board->res_addr + XILINX_VERINT) >> 4; | ||
239 | init_step = 2; | ||
240 | |||
241 | /* Assert PTADR# - we're in passive mode so the other bits are | ||
242 | not important */ | ||
243 | iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR); | ||
244 | |||
245 | /* Enable interrupts from card */ | ||
246 | kvaser_pci_enable_irq(dev); | ||
247 | } else { | ||
248 | struct sja1000_priv *master_priv = netdev_priv(*master_dev); | ||
249 | struct kvaser_pci *master_board = master_priv->priv; | ||
250 | master_board->slave_dev[channel - 1] = dev; | ||
251 | master_board->no_channels = channel + 1; | ||
252 | board->xilinx_ver = master_board->xilinx_ver; | ||
253 | } | ||
254 | |||
255 | dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES; | ||
256 | |||
257 | priv->read_reg = kvaser_pci_read_reg; | ||
258 | priv->write_reg = kvaser_pci_write_reg; | ||
259 | |||
260 | priv->can.clock.freq = KVASER_PCI_CAN_CLOCK; | ||
261 | |||
262 | priv->ocr = KVASER_PCI_OCR; | ||
263 | priv->cdr = KVASER_PCI_CDR; | ||
264 | |||
265 | priv->irq_flags = IRQF_SHARED; | ||
266 | dev->irq = pdev->irq; | ||
267 | |||
268 | init_step = 4; | ||
269 | |||
270 | dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n", | ||
271 | dev->base_addr, board->conf_addr, dev->irq); | ||
272 | |||
273 | SET_NETDEV_DEV(dev, &pdev->dev); | ||
274 | |||
275 | /* Register SJA1000 device */ | ||
276 | err = register_sja1000dev(dev); | ||
277 | if (err) { | ||
278 | dev_err(&pdev->dev, "Registering device failed (err=%d)\n", | ||
279 | err); | ||
280 | goto failure; | ||
281 | } | ||
282 | |||
283 | if (channel == 0) | ||
284 | *master_dev = dev; | ||
285 | |||
286 | return 0; | ||
287 | |||
288 | failure: | ||
289 | kvaser_pci_del_chan(dev); | ||
290 | return err; | ||
291 | } | ||
292 | |||
293 | static int __devinit kvaser_pci_init_one(struct pci_dev *pdev, | ||
294 | const struct pci_device_id *ent) | ||
295 | { | ||
296 | int err; | ||
297 | struct net_device *master_dev = NULL; | ||
298 | struct sja1000_priv *priv; | ||
299 | struct kvaser_pci *board; | ||
300 | int no_channels; | ||
301 | void __iomem *base_addr = NULL; | ||
302 | void __iomem *conf_addr = NULL; | ||
303 | void __iomem *res_addr = NULL; | ||
304 | int i; | ||
305 | |||
306 | dev_info(&pdev->dev, "initializing device %04x:%04x\n", | ||
307 | pdev->vendor, pdev->device); | ||
308 | |||
309 | err = pci_enable_device(pdev); | ||
310 | if (err) | ||
311 | goto failure; | ||
312 | |||
313 | err = pci_request_regions(pdev, DRV_NAME); | ||
314 | if (err) | ||
315 | goto failure_release_pci; | ||
316 | |||
317 | /* S5920 */ | ||
318 | conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE); | ||
319 | if (conf_addr == NULL) { | ||
320 | err = -ENODEV; | ||
321 | goto failure_release_regions; | ||
322 | } | ||
323 | |||
324 | /* XILINX board wide address */ | ||
325 | res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE); | ||
326 | if (res_addr == NULL) { | ||
327 | err = -ENOMEM; | ||
328 | goto failure_iounmap; | ||
329 | } | ||
330 | |||
331 | base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE); | ||
332 | if (base_addr == NULL) { | ||
333 | err = -ENOMEM; | ||
334 | goto failure_iounmap; | ||
335 | } | ||
336 | |||
337 | no_channels = number_of_sja1000_chip(base_addr); | ||
338 | if (no_channels == 0) { | ||
339 | err = -ENOMEM; | ||
340 | goto failure_iounmap; | ||
341 | } | ||
342 | |||
343 | for (i = 0; i < no_channels; i++) { | ||
344 | err = kvaser_pci_add_chan(pdev, i, &master_dev, | ||
345 | conf_addr, res_addr, | ||
346 | (unsigned long)base_addr); | ||
347 | if (err) | ||
348 | goto failure_cleanup; | ||
349 | } | ||
350 | |||
351 | priv = netdev_priv(master_dev); | ||
352 | board = priv->priv; | ||
353 | |||
354 | dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n", | ||
355 | board->xilinx_ver, board->no_channels); | ||
356 | |||
357 | pci_set_drvdata(pdev, master_dev); | ||
358 | return 0; | ||
359 | |||
360 | failure_cleanup: | ||
361 | kvaser_pci_del_chan(master_dev); | ||
362 | |||
363 | failure_iounmap: | ||
364 | if (conf_addr != NULL) | ||
365 | pci_iounmap(pdev, conf_addr); | ||
366 | if (res_addr != NULL) | ||
367 | pci_iounmap(pdev, res_addr); | ||
368 | if (base_addr != NULL) | ||
369 | pci_iounmap(pdev, base_addr); | ||
370 | |||
371 | failure_release_regions: | ||
372 | pci_release_regions(pdev); | ||
373 | |||
374 | failure_release_pci: | ||
375 | pci_disable_device(pdev); | ||
376 | |||
377 | failure: | ||
378 | return err; | ||
379 | |||
380 | } | ||
381 | |||
382 | static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev) | ||
383 | { | ||
384 | struct net_device *dev = pci_get_drvdata(pdev); | ||
385 | |||
386 | kvaser_pci_del_chan(dev); | ||
387 | |||
388 | pci_release_regions(pdev); | ||
389 | pci_disable_device(pdev); | ||
390 | pci_set_drvdata(pdev, NULL); | ||
391 | } | ||
392 | |||
393 | static struct pci_driver kvaser_pci_driver = { | ||
394 | .name = DRV_NAME, | ||
395 | .id_table = kvaser_pci_tbl, | ||
396 | .probe = kvaser_pci_init_one, | ||
397 | .remove = __devexit_p(kvaser_pci_remove_one), | ||
398 | }; | ||
399 | |||
400 | static int __init kvaser_pci_init(void) | ||
401 | { | ||
402 | return pci_register_driver(&kvaser_pci_driver); | ||
403 | } | ||
404 | |||
405 | static void __exit kvaser_pci_exit(void) | ||
406 | { | ||
407 | pci_unregister_driver(&kvaser_pci_driver); | ||
408 | } | ||
409 | |||
410 | module_init(kvaser_pci_init); | ||
411 | module_exit(kvaser_pci_exit); | ||