]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/tscan1.c
changed usb vendor and product id.
[lincan.git] / lincan / src / tscan1.c
1 /* tscan1.c
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  * The support for TS-CAN1 and TS-7KV provided by Ronald Gomes
10  * from Technologic Systems <http://www.embeddedarm.com/>
11  */
12
13 #include "../include/can.h"
14 #include "../include/can_sysdep.h"
15 #include "../include/main.h"
16 #include "../include/pcm3680.h"
17 #include "../include/sja1000p.h"
18
19 #include <linux/module.h>
20 #include "../include/modparms.h"
21 #include "../include/devcommon.h"
22
23 #include "../include/tscan1.h"
24
25 static CAN_DEFINE_SPINLOCK(ts7kv_win_lock);
26
27 long clock[MAX_HW_CARDS]={-1,-1,-1,-1,-1,-1,-1,-1};
28 MODULE_PARM(clock, "1-" __MODULE_STRING(MAX_HW_CARDS)"i");
29 MODULE_PARM_DESC(clock,"clock frequency for each board in step of 1kHz");
30
31 long tscanio[MAX_HW_CARDS]={-1,-1,-1,-1,-1,-1,-1,-1};
32 MODULE_PARM(tscanio, "1-" __MODULE_STRING(MAX_HW_CARDS)"i");
33 MODULE_PARM_DESC(tscanio,"TSCAN CAN controller IO address for each board");
34
35 #if defined(TS7XXX_IO8_BASE)&&defined(TSXXX_BASE_IO)
36 int tsxxx_base=TS7XXX_IO8_BASE+TSXXX_BASE_IO;
37 #elif defined(TS7XXX_IO8_BASE)
38 int tsxxx_base=TS7XXX_IO8_BASE;
39 #else /*TS7XXX_IO8_BASE*/
40 unsigned long tsxxx_base=0;
41 #endif /*TS7XXX_IO8_BASE*/
42 MODULE_PARM(tsxxx_base, "1i");
43 MODULE_PARM_DESC(tsxxx_base,"The base of the ISA/8-bit IO space for TSxxx CAN peripherals in the system");
44
45 unsigned short ts7kv_isused = 0x0;
46
47 /**
48  * tscan1_request_io: - reserve io or memory range for can board
49  * @candev: pointer to candevice/board which asks for io. Field @io_addr
50  *      of @candev is used in most cases to define start of the range
51  *
52  * The function tscan1_request_io() is used to reserve the io-memory. If your
53  * hardware uses a dedicated memory range as hardware control registers you
54  * will have to add the code to reserve this memory as well.
55  * %IO_RANGE is the io-memory range that gets reserved, please adjust according
56  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
57  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
58  * Return Value: The function returns zero on success or %-ENODEV on failure
59  * File: src/tscan1.c
60  */
61 int tscan1_request_io(struct candevice_t *candev)
62 {
63         unsigned long can_io_addr;
64         unsigned long remap_can_io_addr = 0;
65         unsigned char mode;
66         int i, j;
67
68
69         if (!can_request_io_region(candev->io_addr, TSXXX_IO_RANGE, "tscan1-base")) {
70                 CANMSG("Unable to request base IO port: 0x%lx\n", candev->io_addr);
71                 return -ENODEV;
72         } else {
73                 DEBUGMSG("Registered base IO port: 0x%lx - 0x%lx\n",
74                         candev->io_addr, candev->io_addr+TSXXX_IO_RANGE-1);
75         }
76
77         can_io_addr = tscanio[candev->candev_idx];
78
79         if(can_io_addr && (can_io_addr != (unsigned long)-1)) {
80                 remap_can_io_addr = tsxxx_base + can_io_addr;
81
82                 if (!can_request_io_region(remap_can_io_addr, TSXXX_CAN_RANGE, "tscan1-can")) {
83                         CANMSG("Unable to request CAN IO port: 0x%lx\n", remap_can_io_addr);
84                         can_release_io_region(candev->io_addr, TSXXX_IO_RANGE);
85                         return -ENODEV;
86                 } else {
87                         DEBUGMSG("Registered CAN IO port: 0x%lx - 0x%lx\n",
88                         remap_can_io_addr, remap_can_io_addr+TSXXX_CAN_RANGE-1);
89                 }
90         } else {
91                 for(i = 0; 1; i++) {
92
93                         if(i>=8) {
94                                 CANMSG("Unable find range for CAN IO port\n");
95                                 can_release_io_region(candev->io_addr, TSXXX_IO_RANGE);
96                                 return -ENODEV;
97                         }
98
99                         can_io_addr = 0x100 + i * TSXXX_CAN_RANGE;
100                         for (j = 0; j < MAX_HW_CARDS; j++) {
101                                 if(tscanio[j] == can_io_addr) {
102                                         j = -1;
103                                         break;
104                                 }
105                         }
106                         if(j<0)
107                                 continue;
108
109                         remap_can_io_addr = tsxxx_base + can_io_addr;
110
111                         if (can_request_io_region(remap_can_io_addr, TSXXX_CAN_RANGE, "tscan1-can"))
112                                 break;
113                 }
114
115                 tscanio[candev->candev_idx] = can_io_addr;
116
117                 DEBUGMSG("Found free range and registered CAN IO port: 0x%lx - 0x%lx\n",
118                         remap_can_io_addr, remap_can_io_addr+TSXXX_CAN_RANGE-1);
119         }
120
121         candev->chip[0]->chip_base_addr = remap_can_io_addr;
122         candev->chip[0]->msgobj[0]->obj_base_addr = remap_can_io_addr;
123
124         switch(can_io_addr) {
125                 case 0x100:     mode=0x60; break;
126                 case 0x120:     mode=0x61; break;
127                 case 0x180:     mode=0x62; break;
128                 case 0x1A0:     mode=0x63; break;
129                 case 0x200:     mode=0x64; break;
130                 case 0x240:     mode=0x65; break;
131                 case 0x280:     mode=0x66; break;
132                 case 0x320:     mode=0x67; break;
133                 default:        mode=0x60; break;
134         }
135
136         outb(0x00, candev->io_addr+TSCAN1_WIN_REG);
137         outb(mode, candev->io_addr+TSCAN1_MOD_REG);
138
139         return 0;
140 }
141
142 int ts7kv_request_io(struct candevice_t *candev)
143 {
144         if (!can_request_io_region(candev->io_addr, TSXXX_CAN_RANGE, "ts7kv-can")) {
145                 CANMSG("Unable to request CAN IO port: 0x%lx\n", candev->io_addr);
146                 return -ENODEV;
147         } else {
148                 DEBUGMSG("Registered CAN IO port: 0x%lx - 0x%lx\n",
149                         candev->io_addr, candev->io_addr+TSXXX_CAN_RANGE-1);
150         }
151
152         return 0;
153 }
154
155 /**
156  * tscan1_release_io - free reserved io memory range
157  * @candev: pointer to candevice/board which releases io
158  *
159  * The function tscan1_release_io() is used to free reserved io-memory.
160  * In case you have reserved more io memory, don't forget to free it here.
161  * IO_RANGE is the io-memory range that gets released, please adjust according
162  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
163  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
164  * Return Value: The function always returns zero
165  * File: src/tscan1.c
166  */
167 int tscan1_release_io(struct candevice_t *candev)
168 {
169         unsigned long remap_can_io_addr;
170
171         if(candev->chip[0]){
172                 remap_can_io_addr = candev->chip[0]->chip_base_addr;
173                 if(remap_can_io_addr != (unsigned long)-1)
174                         can_release_io_region(remap_can_io_addr, TSXXX_CAN_RANGE);
175         }
176
177         outb(0x20, candev->io_addr+TSCAN1_MOD_REG);
178
179         can_release_io_region(candev->io_addr, TSXXX_IO_RANGE);
180         return 0;
181 }
182
183 int ts7kv_release_io(struct candevice_t *candev)
184 {
185         can_release_io_region(candev->io_addr, TSXXX_CAN_RANGE);
186         return 0;
187 }
188
189 /**
190  * tscan1_reset - hardware reset routine
191  * @candev: Pointer to candevice/board structure
192  *
193  * The function tscan1_reset() is used to give a hardware reset. This is
194  * rather hardware specific so I haven't included example code. Don't forget to
195  * check the reset status of the chip before returning.
196  * Return Value: The function returns zero on success or %-ENODEV on failure
197  * File: src/tscan1.c
198  */
199 int tscan1_reset(struct candevice_t *candev)
200 {
201         unsigned short i=0, chipnr;
202         struct canchip_t *chip;
203
204         DEBUGMSG("Resetting %s hardware ...\n", candev->hwname);
205
206         for(chipnr=0;chipnr<candev->nr_sja1000_chips;chipnr++) {
207                 chip=candev->chip[chipnr];
208                 can_write_reg(chip, sjaMOD_RM, SJAMOD);
209                 udelay(1000);
210                 can_write_reg(chip, 0x00, SJAIER);
211                 udelay(1000);
212                 i=20;
213                 while (can_read_reg(chip, SJAMOD)&sjaMOD_RM){
214                         if(!i--) return -ENODEV;
215                         udelay(1000);
216                         can_write_reg(chip, 0, SJAMOD);
217                 }
218                 udelay(1000);
219                 can_write_reg(chip, sjaCDR_PELICAN, SJACDR);
220                 can_write_reg(chip, 0x00, SJAIER);
221         }
222
223         return 0;
224 }
225
226 #define RESET_ADDR 0x100
227 #define NR_82527 0
228 #define NR_SJA1000 1
229
230 int tscan1_check_presence(unsigned long remap_io_addr, int *pjmp)
231 {
232         int result = -ENODEV;
233
234         if (!can_request_io_region(remap_io_addr, TSXXX_IO_RANGE, "tscan1-probe"))
235                 return -ENODEV;
236
237         do {
238                 if (inb(remap_io_addr+TSXXX_ID0_REG)!=TSCAN1_ID0 ||
239                         inb(remap_io_addr+TSXXX_ID1_REG)!=TSCAN1_ID1)
240                         break;
241
242                 outb(0x00, remap_io_addr+TSCAN1_WIN_REG);
243                 outb(0x20, remap_io_addr+TSCAN1_MOD_REG);
244
245                 if(pjmp)
246                         *pjmp = inb(remap_io_addr+TSCAN1_JMP_REG);
247
248                 result = 0;
249         } while (0);
250
251         can_release_io_region(remap_io_addr, TSXXX_IO_RANGE);
252
253         return result;
254 }
255
256
257 /**
258  * tscan1_init_hw_data - Initialize hardware cards
259  * @candev: Pointer to candevice/board structure
260  *
261  * The function tscan1_init_hw_data() is used to initialize the hardware
262  * structure containing information about the installed CAN-board.
263  * %RESET_ADDR represents the io-address of the hardware reset register.
264  * %NR_82527 represents the number of intel 82527 chips on the board.
265  * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
266  * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
267  * the hardware uses programmable interrupts.
268  * Return Value: The function always returns zero
269  * File: src/tscan1.c
270  */
271
272 int tscan1_init_hw_data(struct candevice_t *candev)
273 {
274         int i, j, jmp;
275         unsigned long io_addr;
276         unsigned long remap_io_addr;
277
278         io_addr = candev->io_addr;
279
280         if(io_addr && (io_addr != (unsigned long)-1)) {
281                 remap_io_addr = io_addr = tsxxx_base;
282
283                 if(tscan1_check_presence(remap_io_addr, &jmp)){
284                         CANMSG("No TSCAN1 card found at address 0xlx\n");
285                         return -ENODEV;
286                 }
287         } else {
288                 DEBUGMSG("Scanning bus for TS-CAN1 boards...\n");
289
290                 for (i=0; 1;i++)
291                 {
292                         if(i >= 4) {
293                                 CANMSG("No TS-CAN1 boards found for slot %d\n", candev->candev_idx);
294                                 return -ENODEV;
295                         }
296
297                         io_addr = TSCAN1_BASE_IO + i*TSXXX_IO_RANGE;
298                         remap_io_addr = io_addr = tsxxx_base;
299
300                         for (j = 0; j < MAX_HW_CARDS; j++) {
301                                 if(io[j] == io_addr){
302                                         j = -1;
303                                         break;
304                                 }
305                         }
306                         if(j<0)
307                                 continue;
308
309                         if(!tscan1_check_presence(remap_io_addr, &jmp))
310                                 break;
311
312                 }
313                 DEBUGMSG("TS-CAN1 board was found at 0x%lx for driver slot %d\n",
314                                         io_addr, candev->candev_idx);
315
316                 io[candev->candev_idx] = io_addr;
317         }
318
319         candev->io_addr = remap_io_addr;
320         /* unused reset address is used to store jumper setting */
321         candev->res_addr = jmp;
322
323         candev->nr_82527_chips=NR_82527;
324         candev->nr_sja1000_chips=NR_SJA1000;
325         candev->nr_all_chips=NR_82527+NR_SJA1000;
326         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
327
328         DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
329                 candev->io_addr, candev->candev_idx, candev->hwname);
330
331         return 0;
332 }
333
334
335 int ts7kv_check_presence(unsigned long remap_io_addr, int *pjmp)
336 {
337         int result = -ENODEV;
338
339         if (!can_request_io_region(remap_io_addr, TSXXX_IO_RANGE, "ts7kv-probe"))
340                 return -ENODEV;
341
342         do {
343                 if (inb(remap_io_addr+TSXXX_ID0_REG)!=TS7KV_ID0 ||
344                         inb(remap_io_addr+TSXXX_ID1_REG)!=TS7KV_ID1)
345                         break;
346
347                 if(pjmp)
348                         *pjmp = inb(remap_io_addr+TS7KV_JMP_REG);
349
350                 result = 0;
351         } while (0);
352
353         can_release_io_region(remap_io_addr, TSXXX_IO_RANGE);
354
355         return result;
356 }
357
358 int ts7kv_init_hw_data(struct candevice_t *candev)
359 {
360         int i, j, jmp;
361         unsigned long io_addr;
362         unsigned long remap_io_addr;
363         unsigned long can_io_addr;
364
365         io_addr = candev->io_addr;
366
367         if(io_addr && (io_addr != (unsigned long)-1)) {
368                 remap_io_addr = io_addr = tsxxx_base;
369
370                 if(ts7kv_check_presence(remap_io_addr, &jmp)){
371                         CANMSG("No TS7KV card found at address 0xlx\n");
372                         return -ENODEV;
373                 }
374         } else {
375                 DEBUGMSG("Scanning bus for TS7KV boards...\n");
376
377                 for (i=0; 1;i++)
378                 {
379                         if(i >= 4) {
380                                 CANMSG("No TS7KV boards found for slot %d\n", candev->candev_idx);
381                                 return -ENODEV;
382                         }
383
384                         io_addr = TS7KV_BASE_IO + i*TSXXX_IO_RANGE;
385                         remap_io_addr = io_addr = tsxxx_base;
386
387                         for (j = 0; j < MAX_HW_CARDS; j++) {
388                                 if(io[j] == io_addr){
389                                         j = -1;
390                                         break;
391                                 }
392                         }
393                         if(j<0)
394                                 continue;
395
396                         if(!ts7kv_check_presence(remap_io_addr, &jmp))
397                                 break;
398
399                 }
400                 DEBUGMSG("TS7KV board was found at 0x%lx for driver slot %d\n",
401                                         io_addr, candev->candev_idx);
402
403                 io[candev->candev_idx] = io_addr;
404         }
405
406         can_io_addr = ((io_addr>>3)&0x03)*0x20;
407         tscanio[candev->candev_idx] = can_io_addr;
408
409         /* dev_base_addr address is used to store remapped PLD base address */
410         candev->dev_base_addr = remap_io_addr;
411
412         /* dev_base_addr address is used to store remapped slave window address */
413         candev->io_addr = can_io_addr+tsxxx_base;
414
415         /* unused reset address is used to store jumper setting */
416         candev->res_addr = jmp;
417
418         candev->nr_82527_chips=NR_82527;
419         candev->nr_sja1000_chips=NR_SJA1000;
420         candev->nr_all_chips=NR_82527+NR_SJA1000;
421         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
422
423         DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
424                 candev->io_addr, candev->candev_idx, candev->hwname);
425
426         return 0;
427 }
428
429 /**
430  * tscan1_init_chip_data - Initialize chips
431  * @candev: Pointer to candevice/board structure
432  * @chipnr: Number of the CAN chip on the hardware card
433  *
434  * The function tscan1_init_chip_data() is used to initialize the hardware
435  * structure containing information about the CAN chips.
436  * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
437  * "sja1000".
438  * The @chip_base_addr entry represents the start of the 'official' memory map
439  * of the installed chip. It's likely that this is the same as the @io_addr
440  * argument supplied at module loading time.
441  * The @clock entry holds the chip clock value in Hz.
442  * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
443  * register. Options defined in the %sja1000.h file:
444  * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
445  * The entry @sja_ocr_reg holds hardware specific options for the Output Control
446  * register. Options defined in the %sja1000.h file:
447  * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
448  * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
449  * The entry @int_clk_reg holds hardware specific options for the Clock Out
450  * register. Options defined in the %i82527.h file:
451  * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
452  * The entry @int_bus_reg holds hardware specific options for the Bus
453  * Configuration register. Options defined in the %i82527.h file:
454  * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
455  * Return Value: The function always returns zero
456  * File: src/tscan1.c
457  */
458 int tscan1_init_chip_data(struct candevice_t *candev, int chipnr)
459 {
460         unsigned long clk;
461         int jmp;
462         int irq = -1;
463
464         clk = clock[candev->candev_idx];
465         if(!clk || (clk == -1))
466                 clk = 16000 * 1000;
467         else
468                 clk *= 1000;
469
470         /* unused reset address is used to store jumper setting */
471         jmp = candev->res_addr;
472
473         if (jmp&0x10 && jmp&0x20) irq=TSXXX_IRQ5;
474         else if (jmp&0x10) irq=TSXXX_IRQ6;
475         else if (jmp&0x20) irq=TSXXX_IRQ7;
476
477         if(irq<0) {
478                 CANMSG("Jumpers select no IRQ for TSCAN1 at 0x%lx of driver %d/%s\n",
479                         candev->io_addr, candev->candev_idx, candev->hwname);
480                 return -ENODEV;
481         }
482         candev->chip[chipnr]->chip_irq = irq;
483
484         sja1000p_fill_chipspecops(candev->chip[chipnr]);
485
486         candev->chip[chipnr]->clock = clk;
487         candev->chip[chipnr]->int_clk_reg = 0x0;
488         candev->chip[chipnr]->int_bus_reg = 0x0;
489         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
490         candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
491         /* 
492          * The address is assigned during tscan1_request_io()
493          * according to found free ranges or tscanio option
494          */
495         candev->chip[chipnr]->chip_base_addr = (unsigned long)-1;
496
497         return 0;
498 }
499
500 int ts7kv_init_chip_data(struct candevice_t *candev, int chipnr)
501 {
502         unsigned long clk;
503         int jmp;
504         int irq = -1;
505
506         clk = clock[candev->candev_idx];
507         if(!clk || (clk == -1))
508                 clk = 24000 * 1000;
509         else
510                 clk *= 1000;
511
512         /* unused reset address is used to store jumper setting */
513         jmp = candev->res_addr;
514
515         if (jmp&0x10 && jmp&0x20) irq=TSXXX_IRQ5;
516         else if (jmp&0x10) irq=TSXXX_IRQ6;
517         else if (jmp&0x20) irq=TSXXX_IRQ7;
518
519         if(irq<0) {
520                 CANMSG("Jumpers select no IRQ for TS7KV CAN at 0x%lx of driver %d/%s\n",
521                         candev->io_addr, candev->candev_idx, candev->hwname);
522                 return -ENODEV;
523         }
524
525         candev->chip[chipnr]->chip_irq = irq;
526
527         sja1000p_fill_chipspecops(candev->chip[chipnr]);
528
529         candev->chip[chipnr]->clock = clk;
530         candev->chip[chipnr]->int_clk_reg = 0x0;
531         candev->chip[chipnr]->int_bus_reg = 0x0;
532         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
533         candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
534         candev->chip[chipnr]->chip_base_addr = candev->io_addr;
535
536         return 0;
537 }
538
539 /**
540  * tscan1_init_obj_data - Initialize message buffers
541  * @chip: Pointer to chip specific structure
542  * @objnr: Number of the message buffer
543  *
544  * The function tscan1_init_obj_data() is used to initialize the hardware
545  * structure containing information about the different message objects on the
546  * CAN chip. In case of the sja1000 there's only one message object but on the
547  * i82527 chip there are 15.
548  * The code below is for a i82527 chip and initializes the object base addresses
549  * The entry @obj_base_addr represents the first memory address of the message
550  * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
551  * base address.
552  * Unless the hardware uses a segmented memory map, flags can be set zero.
553  * Return Value: The function always returns zero
554  * File: src/tscan1.c
555  */
556 int tscan1_init_obj_data(struct canchip_t *chip, int objnr)
557 {
558         chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
559         return 0;
560 }
561
562 /**
563  * tscan1_program_irq - program interrupts
564  * @candev: Pointer to candevice/board structure
565  *
566  * The function tscan1_program_irq() is used for hardware that uses
567  * programmable interrupts. If your hardware doesn't use programmable interrupts
568  * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
569  * leave this function unedited. Again this function is hardware specific so
570  * there's no example code.
571  * Return value: The function returns zero on success or %-ENODEV on failure
572  * File: src/tscan1.c
573  */
574 int tscan1_program_irq(struct candevice_t *candev)
575 {
576         return 0;
577 }
578
579 /**
580  * tscan1_write_register - Low level write register routine
581  * @data: data to be written
582  * @address: memory address to write to
583  *
584  * The function tscan1_write_register() is used to write to hardware registers
585  * on the CAN chip. You should only have to edit this function if your hardware
586  * uses some specific write process.
587  * Return Value: The function does not return a value
588  * File: src/tscan1.c
589  */
590 void tscan1_write_register(unsigned data, unsigned long address)
591 {
592         outb(data, address);
593 }
594
595 void ts7kv_write_register(unsigned data, unsigned long address)
596 {
597         unsigned long base = address & ~0x1f;
598         unsigned char nwin = 0x10;
599         unsigned char savewin;
600         
601         can_spin_irqflags_t flags;
602
603         if((address&0x1f) > 0x1d) {
604                 nwin++;
605                 address -= 0x10;
606         }
607
608         can_spin_lock_irqsave(&ts7kv_win_lock,flags);
609         savewin = inb(base+TS7KV_WIN_REG);
610         if(nwin == savewin) {
611                 outb(data, address);
612         }else{
613                 outb(nwin, base+TS7KV_WIN_REG);
614                 outb(data, address);
615                 outb(savewin, base+TS7KV_WIN_REG);
616         }
617         can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
618 }
619
620 /**
621  * tscan1_read_register - Low level read register routine
622  * @address: memory address to read from
623  *
624  * The function tscan1_read_register() is used to read from hardware registers
625  * on the CAN chip. You should only have to edit this function if your hardware
626  * uses some specific read process.
627  * Return Value: The function returns the value stored in @address
628  * File: src/tscan1.c
629  */
630 unsigned tscan1_read_register(unsigned long address)
631 {
632         return inb(address);
633 }
634
635 unsigned ts7kv_read_register(unsigned long address)
636 {
637         unsigned long base = address & ~0x1f;
638         unsigned char nwin = 0x10;
639         unsigned char savewin;
640         unsigned val;
641         
642         can_spin_irqflags_t flags;
643
644         if((address&0x1f) > 0x1d) {
645                 nwin++;
646                 address -= 0x10;
647         }
648
649         can_spin_lock_irqsave(&ts7kv_win_lock,flags);
650         savewin = inb(base+TS7KV_WIN_REG);
651         if(nwin == savewin) {
652                 val = inb(address);
653         }else{
654                 outb(nwin, base+TS7KV_WIN_REG);
655                 val = inb(address);
656                 outb(savewin, base+TS7KV_WIN_REG);
657         }
658         can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
659
660         return val;
661 }
662
663 int tscan1_register(struct hwspecops_t *hwspecops)
664 {
665         hwspecops->request_io = tscan1_request_io;
666         hwspecops->release_io = tscan1_release_io;
667         hwspecops->reset = tscan1_reset;
668         hwspecops->init_hw_data = tscan1_init_hw_data;
669         hwspecops->init_chip_data = tscan1_init_chip_data;
670         hwspecops->init_obj_data = tscan1_init_obj_data;
671         hwspecops->write_register = tscan1_write_register;
672         hwspecops->read_register = tscan1_read_register;
673         hwspecops->program_irq = tscan1_program_irq;
674         return 0;
675 }
676
677 extern int ts7kv_register(struct hwspecops_t *hwspecops)
678 {
679         hwspecops->request_io = ts7kv_request_io;
680         hwspecops->release_io = ts7kv_release_io;
681         hwspecops->reset = tscan1_reset;
682         hwspecops->init_hw_data = ts7kv_init_hw_data;
683         hwspecops->init_chip_data = ts7kv_init_chip_data;
684         hwspecops->init_obj_data = tscan1_init_obj_data;
685         hwspecops->write_register = ts7kv_write_register;
686         hwspecops->read_register = ts7kv_read_register;
687         hwspecops->program_irq = tscan1_program_irq;
688         return 0;
689 }