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