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