]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/esdpci266.c
770ffd881ddb52caa9f57620d2ca1c159d5eaa4f
[lincan.git] / lincan / src / esdpci266.c
1 /**************************************************************************/
2 /* File: esdpci266.c - support for ESD PCI/PMC 266 cards                  */
3 /*                                                                        */
4 /* LinCAN - (Not only) Linux CAN bus driver                               */
5 /* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz>   */
6 /* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
7 /* Funded by OCERA and FRESCOR IST projects                               */
8 /* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
9 /*                                                                        */
10 /* LinCAN is free software; you can redistribute it and/or modify it      */
11 /* under terms of the GNU General Public License as published by the      */
12 /* Free Software Foundation; either version 2, or (at your option) any    */
13 /* later version.  LinCAN is distributed in the hope that it will be      */
14 /* useful, but WITHOUT ANY WARRANTY; without even the implied warranty    */
15 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU    */
16 /* General Public License for more details. You should have received a    */
17 /* copy of the GNU General Public License along with LinCAN; see file     */
18 /* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
19 /* Cambridge, MA 02139, USA.                                              */
20 /*                                                                        */
21 /* To allow use of LinCAN in the compact embedded systems firmware        */
22 /* and RT-executives (RTEMS for example), main authors agree with next    */
23 /* special exception:                                                     */
24 /*                                                                        */
25 /* Including LinCAN header files in a file, instantiating LinCAN generics */
26 /* or templates, or linking other files with LinCAN objects to produce    */
27 /* an application image/executable, does not by itself cause the          */
28 /* resulting application image/executable to be covered by                */
29 /* the GNU General Public License.                                        */
30 /* This exception does not however invalidate any other reasons           */
31 /* why the executable file might be covered by the GNU Public License.    */
32 /* Publication of enhanced or derived LinCAN files is required although.  */
33 /**************************************************************************/
34
35 #include "../include/can.h"
36 #include "../include/can_sysdep.h"
37 #include "../include/main.h"
38 #include "../include/sja1000p.h"
39
40 #ifdef CAN_ENABLE_PCI_SUPPORT
41
42 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
43 #define ioread32        can_readl
44 #define iowrite32       can_writel
45 #define ioread8         can_readb
46 #define iowrite8        can_writeb
47 #define wmb()
48 #define rmb()
49 #endif
50
51 #define PLX_9056_VENDOR_ID   0x10B5
52 #define PLX_9056_DEVICE_ID   0x9056
53 #define ESDPCI266_PCI_VENDOR_ID    0x12FE
54 #define ESDPCI266_PCI_PRODUCT_ID   0x000E
55
56 /* PCI to local bus bridge PLX9050 */
57 #define ESDPCI266_BYTES_PER_CIRCUIT 0x100
58
59 void esdpci266_enable_irq(struct candevice_t *candev)
60 {
61         uint32_t intcsr;
62         can_ioptr_t addr = candev->aux_base_addr + 0x68;
63         intcsr = ioread32(addr);
64         intcsr |= 0x900;
65         iowrite32(intcsr, addr);
66 }
67
68 void esdpci266_disable_irq(struct candevice_t *candev)
69 {
70         uint32_t intcsr;
71         can_ioptr_t addr = candev->aux_base_addr + 0x68;
72         intcsr = ioread32(addr);
73         intcsr &= ~0x900;
74         iowrite32(intcsr, addr);
75 }
76
77 int esdpci266_request_io(struct candevice_t *candev)
78 {
79         unsigned long addr, size;
80         can_ioptr_t remap_addr;
81         struct pci_dev *dev = candev->sysdevptr.pcidev;
82
83         /* request memory region for SJA chips window */
84         if (pci_request_region(dev, 2, "ESD PCI/PMC 266 - MEM") != 0) {
85                 printk("lincan: request of memory range failed\n");
86                 return -ENODEV;
87         }
88
89         /* determine address and size of memory window #2 */
90         addr = pci_resource_start(dev, 2);
91         size = pci_resource_len(dev, 2);
92
93         /* remap to kernel address space */
94         remap_addr = ioremap(addr, size);
95
96         if (!remap_addr) {
97                 printk("lincan: Unable to access I/O memory at: 0x%lx\n", addr);
98                 pci_release_region(dev, 2);
99                 return -ENODEV;
100         }
101
102         /* remap base device address */
103         can_base_addr_fixup(candev, remap_addr);
104
105         /* request memory region for PLX registers */
106         if (pci_request_region(dev, 0, "ESD PCI/PMC 266 - PLX9056") != 0) {
107                 printk("lincan: request of memory range failed\n");
108                 iounmap(candev->dev_base_addr);
109                 pci_release_region(dev, 2);
110                 return -ENODEV;
111         }
112
113         /* determine address and size of memory window #2 */
114         addr = pci_resource_start(dev, 0);
115         size = pci_resource_len(dev, 0);
116
117         /* remap to kernel address space */
118         remap_addr = ioremap(addr, size);
119
120         if (!remap_addr) {
121                 printk("lincan: Unable to access I/O memory at: 0x%lx\n", addr);
122                 pci_release_region(dev, 0);
123                 iounmap(candev->dev_base_addr);
124                 pci_release_region(dev, 2);
125                 return -ENODEV;
126         }
127
128         /* save address of PLX regs */
129         candev->aux_base_addr = remap_addr;
130
131         /* enable irqs */
132         esdpci266_enable_irq(candev);
133
134         return 0;
135 }
136
137 int esdpci266_release_io(struct candevice_t *candev)
138 {
139         esdpci266_disable_irq(candev);
140         iounmap(candev->aux_base_addr);
141         pci_release_region(candev->sysdevptr.pcidev, 0);
142         iounmap(candev->dev_base_addr);
143         pci_release_region(candev->sysdevptr.pcidev, 2);
144         return 0;
145 }
146
147 void esdpci266_write_register(unsigned data, can_ioptr_t address)
148 {
149         iowrite8(data, address);
150         wmb();
151 }
152
153 unsigned esdpci266_read_register(can_ioptr_t address)
154 {
155         return ioread8(address);
156 }
157
158 int esdpci266_reset(struct candevice_t *candev)
159 {
160         int i = 0, chip_nr;
161         struct canchip_t *chip;
162         unsigned cdr;
163
164         printk("lincan: resetting ESD PCI/PMC 266 hardware ...\n");
165
166         for (chip_nr = 0; chip_nr < candev->nr_all_chips; chip_nr++) {
167                 if (!candev->chip[chip_nr])
168                         continue;
169
170                 printk("lincan: resetting SJA1000 chip nr. %d\n", chip_nr);
171
172                 chip = candev->chip[chip_nr];
173
174                 esdpci266_write_register(sjaMOD_RM,
175                                          chip->chip_base_addr + SJAMOD);
176                 udelay(1000);
177
178                 cdr = esdpci266_read_register(chip->chip_base_addr + SJACDR);
179                 esdpci266_write_register(cdr | sjaCDR_PELICAN,
180                                          chip->chip_base_addr + SJACDR);
181                 esdpci266_write_register(0, chip->chip_base_addr + SJAIER);
182
183                 i = 20;
184                 esdpci266_write_register(0, chip->chip_base_addr + SJAMOD);
185                 while (esdpci266_read_register(chip->chip_base_addr + SJAMOD) &
186                        sjaMOD_RM) {
187                         if (!i--)
188                                 return -ENODEV;
189                         udelay(1000);
190                         esdpci266_write_register(0,
191                                                  chip->chip_base_addr + SJAMOD);
192                 }
193
194                 cdr = esdpci266_read_register(chip->chip_base_addr + SJACDR);
195                 esdpci266_write_register(cdr | sjaCDR_PELICAN,
196                                          chip->chip_base_addr + SJACDR);
197                 esdpci266_write_register(0, chip->chip_base_addr + SJAIER);
198                 esdpci266_read_register(chip->chip_base_addr + SJAIR);
199         }
200
201         return 0;
202 }
203
204 int esdpci266_init_hw_data(struct candevice_t *candev)
205 {
206         struct pci_dev *pcidev = NULL;
207
208         printk("lincan: search for ESD PCI/PMC 266 board ...\n");
209
210         do {
211                 pcidev =
212                     pci_find_device(PLX_9056_VENDOR_ID, PLX_9056_DEVICE_ID,
213                                     pcidev);
214                 if (pcidev == NULL)
215                         return -ENODEV;
216                 if (pcidev->subsystem_vendor != ESDPCI266_PCI_VENDOR_ID
217                     || pcidev->subsystem_device != ESDPCI266_PCI_PRODUCT_ID) {
218                         printk
219                             ("PLX9056 found, subvendor/subdevice mismatch (%04d:%04d)\n",
220                              pcidev->subsystem_vendor,
221                              pcidev->subsystem_device);
222                         continue;
223                 }
224         } while (can_check_dev_taken(pcidev));
225
226         if (pci_enable_device(pcidev)) {
227                 printk("lincan: pci_enable_device() failed\n");
228                 return -EIO;
229         }
230
231         candev->sysdevptr.pcidev = pcidev;
232
233         candev->io_addr = pci_resource_start(pcidev, 2);
234         candev->dev_base_addr = 0;
235         candev->res_addr = 0;
236         candev->nr_82527_chips = 0;
237         candev->nr_sja1000_chips = 2;
238         candev->nr_all_chips = 2;
239
240         printk("lincan: ESD PCI/PMC 266 board found (%lx)\n", candev->io_addr);
241
242         return 0;
243 }
244
245 int esdpci266_init_chip_data(struct candevice_t *candev, int chipnr)
246 {
247         if (candev->sysdevptr.pcidev == NULL)
248                 return -ENODEV;
249
250         candev->chip[chipnr]->chip_irq = candev->sysdevptr.pcidev->irq;
251         sja1000p_fill_chipspecops(candev->chip[chipnr]);
252         candev->chip[chipnr]->chip_base_addr =
253             candev->dev_base_addr + chipnr * ESDPCI266_BYTES_PER_CIRCUIT;
254         candev->chip[chipnr]->clock = 16000000;
255         candev->chip[chipnr]->int_clk_reg = 0;
256         candev->chip[chipnr]->int_bus_reg = 0;
257         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
258         candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
259         candev->chip[chipnr]->flags |= CHIP_IRQ_PCI;
260         return 0;
261 }
262
263 int esdpci266_init_obj_data(struct canchip_t *chip, int objnr)
264 {
265         chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
266         return 0;
267 }
268
269 int esdpci266_program_irq(struct candevice_t *candev)
270 {
271         return 0;
272 }
273
274 int esdpci266_register(struct hwspecops_t *hwspecops)
275 {
276         hwspecops->request_io = esdpci266_request_io;
277         hwspecops->release_io = esdpci266_release_io;
278         hwspecops->reset = esdpci266_reset;
279         hwspecops->init_hw_data = esdpci266_init_hw_data;
280         hwspecops->init_chip_data = esdpci266_init_chip_data;
281         hwspecops->init_obj_data = esdpci266_init_obj_data;
282         hwspecops->write_register = esdpci266_write_register;
283         hwspecops->read_register = esdpci266_read_register;
284         hwspecops->program_irq = esdpci266_program_irq;
285         return 0;
286 }
287
288 #endif /* CAN_ENABLE_PCI_SUPPORT */