]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/kv_pcican.c
The LinCAN driver license unified according to DCE FEE CTU head and superiors request.
[lincan.git] / lincan / src / kv_pcican.c
1 /**************************************************************************/
2 /* File: kv_pcican.c - support for KVASER PCIcan-S/D/Q 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 #define KV_PCICAN_PCICAN_VENDOR  0x10e8
43 #define KV_PCICAN_PCICAN_ID 0x8406
44
45 /*AMCC 5920*/
46
47 #define S5920_OMB    0x0C
48 #define S5920_IMB    0x1C
49 #define S5920_MBEF   0x34
50 #define S5920_INTCSR 0x38
51 #define S5920_RCR    0x3C
52 #define S5920_PTCR   0x60
53
54 #define INTCSR_ADDON_INTENABLE_M        0x2000
55 #define INTCSR_INTERRUPT_ASSERTED_M     0x800000
56
57 #define KV_PCICAN_BYTES_PER_CIRCUIT 0x20
58
59 // Standard value: Pushpull  (OCTP1|OCTN1|OCTP0|OCTN0|OCM1)
60 #define KV_PCICAN_OCR_DEFAULT_STD 0xDA
61 // For Galathea piggyback.
62 #define KV_PCICAN_OCR_DEFAULT_GAL 0xDB
63
64 /*
65
66 You need to know the following: 
67 " RX1 is connected to ground. 
68 " TX1 is not connected. 
69 " CLKO is not connected. 
70 " Setting the OCR register to 0xDA is a good idea. 
71   This means  normal output mode , push-pull and the correct polarity. 
72 " In the CDR register, you should set CBP to 1. 
73   You will probably also want to set the clock divider value to 0 (meaning divide-by-2),
74   the Pelican bit, and the clock-off bit (you have no need for CLKOUT anyway.)
75
76 */
77
78
79
80 void kv_pcican_disconnect_irq(struct candevice_t *candev)
81 {
82         unsigned long tmp;
83         /* Disable interrupts from card */
84         tmp = can_inl(candev->dev_base_addr + S5920_INTCSR);
85         tmp &= ~INTCSR_ADDON_INTENABLE_M;
86         can_outl(tmp, candev->dev_base_addr + S5920_INTCSR);
87 }
88
89 void kv_pcican_connect_irq(struct candevice_t *candev)
90 {
91         unsigned long tmp;
92         /* Enable interrupts from card */
93         tmp = can_inl(candev->dev_base_addr + S5920_INTCSR);
94         tmp |= INTCSR_ADDON_INTENABLE_M;
95         can_outl(tmp, candev->dev_base_addr + S5920_INTCSR);
96 }
97
98
99 int kv_pcican_request_io(struct candevice_t *candev)
100 {
101     #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
102         if(pci_request_region(candev->sysdevptr.pcidev, 0, "kv_pcican_s5920") != 0){
103                 CANMSG("Request of kv_pcican_s5920 range failed\n");
104                 return -ENODEV;
105         }else if(pci_request_region(candev->sysdevptr.pcidev, 1, "kv_pcican_io") != 0){
106                 CANMSG("Request of kv_pcican_io range failed\n");
107                 goto error_io;
108         }else if(pci_request_region(candev->sysdevptr.pcidev, 2, "kv_pcican_xilinx") != 0){
109                 CANMSG("Request of kv_pcican_xilinx range failed\n");
110                 goto error_xilinx;
111         }
112     #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
113         if(pci_request_regions(candev->sysdevptr.pcidev, "kv_pcican") != 0){
114                 CANMSG("Request of kv_pcican_s5920 regions failed\n");
115                 return -ENODEV;
116         }
117     #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
118
119         kv_pcican_disconnect_irq(candev);
120
121         return 0;
122
123     #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
124     error_xilinx:
125         pci_release_region(candev->sysdevptr.pcidev, 1);
126     error_io:
127         pci_release_region(candev->sysdevptr.pcidev, 0);
128     #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
129         
130         return -ENODEV;
131 }
132
133 int kv_pcican_release_io(struct candevice_t *candev)
134 {
135         kv_pcican_disconnect_irq(candev);
136
137     #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
138         pci_release_region(candev->sysdevptr.pcidev, 2);
139         pci_release_region(candev->sysdevptr.pcidev, 1);
140         pci_release_region(candev->sysdevptr.pcidev, 0);
141     #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
142         pci_release_regions(candev->sysdevptr.pcidev);
143     #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
144
145         return 0;
146 }
147
148
149 void kv_pcican_write_register(unsigned data, can_ioptr_t address)
150 {
151         can_outb(data,address); 
152 }
153
154 unsigned kv_pcican_read_register(can_ioptr_t address)
155 {
156         return can_inb(address);
157 }
158
159 int kv_pcican_reset(struct candevice_t *candev)
160 {
161         int i=0,chip_nr;
162         struct canchip_t *chip;
163         unsigned cdr;
164
165         DEBUGMSG("Resetting kv_pcican hardware ...\n");
166
167         /* Assert PTADR# - we're in passive mode so the other bits are not important */
168         can_outl(0x80808080L, candev->dev_base_addr + S5920_PTCR);
169
170         kv_pcican_disconnect_irq(candev);
171
172         for(chip_nr=0;chip_nr<candev->nr_all_chips;chip_nr++){
173                 if(!candev->chip[chip_nr]) continue;
174                 chip=candev->chip[chip_nr];
175
176                 kv_pcican_write_register(sjaMOD_RM, chip->chip_base_addr+SJAMOD);
177                 udelay(1000);
178
179                 cdr=kv_pcican_read_register(chip->chip_base_addr+SJACDR);
180                 kv_pcican_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
181
182                 kv_pcican_write_register(0, chip->chip_base_addr+SJAIER);
183
184                 i=20;
185                 kv_pcican_write_register(0, chip->chip_base_addr+SJAMOD);
186                 while (kv_pcican_read_register(chip->chip_base_addr+SJAMOD)&sjaMOD_RM){
187                         if(!i--) return -ENODEV;
188                         udelay(1000);
189                         kv_pcican_write_register(0, chip->chip_base_addr+SJAMOD);
190                 }
191
192                 cdr=kv_pcican_read_register(chip->chip_base_addr+SJACDR);
193                 kv_pcican_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
194
195                 kv_pcican_write_register(0, chip->chip_base_addr+SJAIER);
196                 
197                 kv_pcican_read_register(chip->chip_base_addr+SJAIR);
198         }
199         
200
201         kv_pcican_connect_irq(candev);
202
203         return 0;
204 }       
205
206 int kv_pcican_init_hw_data(struct candevice_t *candev)
207 {
208         struct pci_dev *pcidev = NULL;
209         int i;
210
211         do {
212                 pcidev = pci_find_device(KV_PCICAN_PCICAN_VENDOR, KV_PCICAN_PCICAN_ID, pcidev);
213                 if(pcidev == NULL) return -ENODEV;
214         } while(can_check_dev_taken(pcidev));
215         
216         if (pci_enable_device (pcidev)){
217                 printk(KERN_CRIT "Setup of PCICAN failed\n");
218                 return -EIO;
219         }
220         candev->sysdevptr.pcidev=pcidev;
221         
222         for(i=0;i<3;i++){
223                 if(!(pci_resource_flags(pcidev,i)&IORESOURCE_IO)){
224                         printk(KERN_CRIT "PCICAN region %d is not IO\n",i);
225                         return -EIO;
226                 }
227         }
228         candev->dev_base_addr=pci_resource_start(pcidev,0); /*S5920*/
229         candev->io_addr=pci_resource_start(pcidev,1); /*IO window for SJA1000 chips*/
230         candev->res_addr=pci_resource_start(pcidev,2); /*XILINX board wide address*/
231         
232         /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/
233
234         if (!strcmp(candev->hwname,"pcican-s")) {
235                 candev->nr_82527_chips=0;
236                 candev->nr_sja1000_chips=1;
237                 candev->nr_all_chips=1;
238         }
239         if (!strcmp(candev->hwname,"pcican-d")) {
240                 candev->nr_82527_chips=0;
241                 candev->nr_sja1000_chips=2;
242                 candev->nr_all_chips=2;
243         }
244         if (!strcmp(candev->hwname,"pcican-q")) {
245                 candev->nr_82527_chips=0;
246                 candev->nr_sja1000_chips=4;
247                 candev->nr_all_chips=4;
248         }
249
250         return 0;
251 }
252
253 int kv_pcican_init_chip_data(struct candevice_t *candev, int chipnr)
254 {
255
256         if(candev->sysdevptr.pcidev==NULL)
257                 return -ENODEV;
258         
259         candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq;
260
261         sja1000p_fill_chipspecops(candev->chip[chipnr]);
262         candev->chip[chipnr]->chip_base_addr=
263                         candev->io_addr+chipnr*KV_PCICAN_BYTES_PER_CIRCUIT;
264         candev->chip[chipnr]->flags = 0;
265         candev->chip[chipnr]->int_cpu_reg = 0;
266         candev->chip[chipnr]->int_clk_reg = 0;
267         candev->chip[chipnr]->int_bus_reg = 0;
268         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
269         candev->chip[chipnr]->sja_ocr_reg = KV_PCICAN_OCR_DEFAULT_STD;
270         candev->chip[chipnr]->clock = 16000000;
271         candev->chip[chipnr]->flags |= CHIP_IRQ_PCI;
272
273         return 0;
274 }       
275
276 int kv_pcican_init_obj_data(struct canchip_t *chip, int objnr)
277 {
278         chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr;
279         return 0;
280 }
281
282 int kv_pcican_program_irq(struct candevice_t *candev)
283 {
284
285         return 0;
286 }
287
288 int kv_pcican_register(struct hwspecops_t *hwspecops)
289 {
290         hwspecops->request_io = kv_pcican_request_io;
291         hwspecops->release_io = kv_pcican_release_io;
292         hwspecops->reset = kv_pcican_reset;
293         hwspecops->init_hw_data = kv_pcican_init_hw_data;
294         hwspecops->init_chip_data = kv_pcican_init_chip_data;
295         hwspecops->init_obj_data = kv_pcican_init_obj_data;
296         hwspecops->write_register = kv_pcican_write_register;
297         hwspecops->read_register = kv_pcican_read_register;
298         hwspecops->program_irq = kv_pcican_program_irq;
299         return 0;
300 }
301
302
303 #endif /*CAN_ENABLE_PCI_SUPPORT*/