]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/tscan1.c
Corrected print of address in report of card presence check failure.
[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         can_outb(0x00, candev->io_addr+TSCAN1_WIN_REG);
140         can_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         can_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 (can_inb(remap_io_addr+TSXXX_ID0_REG)!=TSCAN1_ID0 ||
242                         can_inb(remap_io_addr+TSXXX_ID1_REG)!=TSCAN1_ID1)
243                         break;
244
245                 can_outb(0x00, remap_io_addr+TSCAN1_WIN_REG);
246                 can_outb(0x20, remap_io_addr+TSCAN1_MOD_REG);
247
248                 if(pjmp)
249                         *pjmp = can_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 0x%lx\n",
288                                 remap_io_addr);
289                         return -ENODEV;
290                 }
291         } else {
292                 DEBUGMSG("Scanning bus for TS-CAN1 boards...\n");
293
294                 for (i=0; 1;i++)
295                 {
296                         if(i >= 4) {
297                                 CANMSG("No TS-CAN1 boards found for slot %d\n", candev->candev_idx);
298                                 return -ENODEV;
299                         }
300
301                         io_addr = TSCAN1_BASE_IO + i*TSXXX_IO_RANGE;
302                         remap_io_addr = io_addr + tsxxx_base;
303
304                         for (j = 0; j < MAX_HW_CARDS; j++) {
305                                 if(io[j] == io_addr){
306                                         j = -1;
307                                         break;
308                                 }
309                         }
310                         if(j<0)
311                                 continue;
312
313                         if(!tscan1_check_presence(remap_io_addr, &jmp))
314                                 break;
315
316                 }
317                 DEBUGMSG("TS-CAN1 board was found at 0x%lx for driver slot %d\n",
318                                         io_addr, candev->candev_idx);
319
320                 io[candev->candev_idx] = io_addr;
321         }
322
323         candev->io_addr = remap_io_addr;
324         /* unused reset address is used to store jumper setting */
325         candev->res_addr = jmp;
326
327         candev->nr_82527_chips=NR_82527;
328         candev->nr_sja1000_chips=NR_SJA1000;
329         candev->nr_all_chips=NR_82527+NR_SJA1000;
330         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
331
332         DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
333                 candev->io_addr, candev->candev_idx, candev->hwname);
334
335         return 0;
336 }
337
338
339 int ts7kv_check_presence(unsigned long remap_io_addr, int *pjmp)
340 {
341         int result = -ENODEV;
342
343         if (!can_request_io_region(remap_io_addr, TSXXX_IO_RANGE, "ts7kv-probe"))
344                 return -ENODEV;
345
346         do {
347                 if (can_inb(remap_io_addr+TSXXX_ID0_REG)!=TS7KV_ID0 ||
348                         can_inb(remap_io_addr+TSXXX_ID1_REG)!=TS7KV_ID1)
349                         break;
350
351                 if(pjmp)
352                         *pjmp = can_inb(remap_io_addr+TS7KV_JMP_REG);
353
354                 result = 0;
355         } while (0);
356
357         can_release_io_region(remap_io_addr, TSXXX_IO_RANGE);
358
359         return result;
360 }
361
362 int ts7kv_init_hw_data(struct candevice_t *candev)
363 {
364         int i, j, jmp;
365         unsigned long io_addr;
366         unsigned long remap_io_addr;
367         unsigned long can_io_addr;
368
369         io_addr = candev->io_addr;
370
371         if(io_addr && (io_addr != (unsigned long)-1)) {
372                 remap_io_addr = io_addr + tsxxx_base;
373
374                 if(ts7kv_check_presence(remap_io_addr, &jmp)){
375                         CANMSG("No TS7KV card found at address 0x%lx\n",
376                                remap_io_addr);
377                         return -ENODEV;
378                 }
379         } else {
380                 DEBUGMSG("Scanning bus for TS7KV boards...\n");
381
382                 for (i=0; 1;i++)
383                 {
384                         if(i >= 4) {
385                                 CANMSG("No TS7KV boards found for slot %d\n", candev->candev_idx);
386                                 return -ENODEV;
387                         }
388
389                         io_addr = TS7KV_BASE_IO + i*TSXXX_IO_RANGE;
390                         remap_io_addr = io_addr + tsxxx_base;
391
392                         for (j = 0; j < MAX_HW_CARDS; j++) {
393                                 if(io[j] == io_addr){
394                                         j = -1;
395                                         break;
396                                 }
397                         }
398                         if(j<0)
399                                 continue;
400
401                         if(!ts7kv_check_presence(remap_io_addr, &jmp))
402                                 break;
403
404                 }
405                 DEBUGMSG("TS7KV board was found at 0x%lx for driver slot %d\n",
406                                         io_addr, candev->candev_idx);
407
408                 io[candev->candev_idx] = io_addr;
409         }
410
411         can_io_addr = ((io_addr>>3)&0x03)*0x20;
412         tscanio[candev->candev_idx] = can_io_addr;
413
414         /* dev_base_addr address is used to store remapped PLD base address */
415         candev->dev_base_addr = remap_io_addr;
416
417         /* dev_base_addr address is used to store remapped slave window address */
418         candev->io_addr = can_io_addr+tsxxx_base;
419
420         /* unused reset address is used to store jumper setting */
421         candev->res_addr = jmp;
422
423         candev->nr_82527_chips=NR_82527;
424         candev->nr_sja1000_chips=NR_SJA1000;
425         candev->nr_all_chips=NR_82527+NR_SJA1000;
426         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
427
428         DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
429                 candev->io_addr, candev->candev_idx, candev->hwname);
430
431         return 0;
432 }
433
434 /**
435  * tscan1_init_chip_data - Initialize chips
436  * @candev: Pointer to candevice/board structure
437  * @chipnr: Number of the CAN chip on the hardware card
438  *
439  * The function tscan1_init_chip_data() is used to initialize the hardware
440  * structure containing information about the CAN chips.
441  * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
442  * "sja1000".
443  * The @chip_base_addr entry represents the start of the 'official' memory map
444  * of the installed chip. It's likely that this is the same as the @io_addr
445  * argument supplied at module loading time.
446  * The @clock entry holds the chip clock value in Hz.
447  * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
448  * register. Options defined in the %sja1000.h file:
449  * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
450  * The entry @sja_ocr_reg holds hardware specific options for the Output Control
451  * register. Options defined in the %sja1000.h file:
452  * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
453  * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
454  * The entry @int_clk_reg holds hardware specific options for the Clock Out
455  * register. Options defined in the %i82527.h file:
456  * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
457  * The entry @int_bus_reg holds hardware specific options for the Bus
458  * Configuration register. Options defined in the %i82527.h file:
459  * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
460  * Return Value: The function always returns zero
461  * File: src/tscan1.c
462  */
463 int tscan1_init_chip_data(struct candevice_t *candev, int chipnr)
464 {
465         unsigned long default_clk = 16000 * 1000;
466         int jmp;
467         int irq = -1;
468
469         /* unused reset address is used to store jumper setting */
470         jmp = candev->res_addr;
471
472         if (jmp&0x10 && jmp&0x20) irq=TSXXX_IRQ5;
473         else if (jmp&0x10) irq=TSXXX_IRQ6;
474         else if (jmp&0x20) irq=TSXXX_IRQ7;
475
476         if(irq<0) {
477                 CANMSG("Jumpers select no IRQ for TSCAN1 at 0x%lx of driver %d/%s\n",
478                         candev->io_addr, candev->candev_idx, candev->hwname);
479                 return -ENODEV;
480         }
481         candev->chip[chipnr]->chip_irq = irq;
482
483         sja1000p_fill_chipspecops(candev->chip[chipnr]);
484
485         if(candev->chip[chipnr]->clock <= 0)
486                 candev->chip[chipnr]->clock = default_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 default_clk = 16000 * 1000;
503         int jmp;
504         int irq = -1;
505
506         /* unused reset address is used to store jumper setting */
507         jmp = candev->res_addr;
508
509         if (jmp&0x10 && jmp&0x20) irq=TSXXX_IRQ5;
510         else if (jmp&0x10) irq=TSXXX_IRQ6;
511         else if (jmp&0x20) irq=TSXXX_IRQ7;
512
513         if(irq<0) {
514                 CANMSG("Jumpers select no IRQ for TS7KV CAN at 0x%lx of driver %d/%s\n",
515                         candev->io_addr, candev->candev_idx, candev->hwname);
516                 return -ENODEV;
517         }
518
519         candev->chip[chipnr]->chip_irq = irq;
520
521         sja1000p_fill_chipspecops(candev->chip[chipnr]);
522
523         if(candev->chip[chipnr]->clock <= 0)
524                 candev->chip[chipnr]->clock = default_clk;
525         candev->chip[chipnr]->int_clk_reg = 0x0;
526         candev->chip[chipnr]->int_bus_reg = 0x0;
527         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
528         candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
529         candev->chip[chipnr]->chip_base_addr = candev->io_addr;
530
531         return 0;
532 }
533
534 /**
535  * tscan1_init_obj_data - Initialize message buffers
536  * @chip: Pointer to chip specific structure
537  * @objnr: Number of the message buffer
538  *
539  * The function tscan1_init_obj_data() is used to initialize the hardware
540  * structure containing information about the different message objects on the
541  * CAN chip. In case of the sja1000 there's only one message object but on the
542  * i82527 chip there are 15.
543  * The code below is for a i82527 chip and initializes the object base addresses
544  * The entry @obj_base_addr represents the first memory address of the message
545  * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
546  * base address.
547  * Unless the hardware uses a segmented memory map, flags can be set zero.
548  * Return Value: The function always returns zero
549  * File: src/tscan1.c
550  */
551 int tscan1_init_obj_data(struct canchip_t *chip, int objnr)
552 {
553         chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
554         return 0;
555 }
556
557 /**
558  * tscan1_program_irq - program interrupts
559  * @candev: Pointer to candevice/board structure
560  *
561  * The function tscan1_program_irq() is used for hardware that uses
562  * programmable interrupts. If your hardware doesn't use programmable interrupts
563  * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
564  * leave this function unedited. Again this function is hardware specific so
565  * there's no example code.
566  * Return value: The function returns zero on success or %-ENODEV on failure
567  * File: src/tscan1.c
568  */
569 int tscan1_program_irq(struct candevice_t *candev)
570 {
571         return 0;
572 }
573
574 /**
575  * tscan1_write_register - Low level write register routine
576  * @data: data to be written
577  * @address: memory address to write to
578  *
579  * The function tscan1_write_register() is used to write to hardware registers
580  * on the CAN chip. You should only have to edit this function if your hardware
581  * uses some specific write process.
582  * Return Value: The function does not return a value
583  * File: src/tscan1.c
584  */
585 void tscan1_write_register(unsigned data, can_ioptr_t address)
586 {
587         can_outb(data, address);
588 }
589
590 void ts7kv_write_register(unsigned data, can_ioptr_t address)
591 {
592         unsigned long addr=can_ioptr2ulong(address);
593         can_ioptr_t base = can_ulong2ioptr(addr & ~0x1f);
594         unsigned char nwin = 0x10;
595         unsigned char savewin;
596         
597         can_spin_irqflags_t flags;
598
599         if((addr&0x1f) > 0x1d) {
600                 nwin++;
601                 address -= 0x10;
602         }
603
604         can_spin_lock_irqsave(&ts7kv_win_lock,flags);
605         savewin = can_inb(base+TS7KV_WIN_REG);
606         if(nwin == savewin) {
607                 can_outb(data, address);
608         }else{
609                 can_outb(nwin, base+TS7KV_WIN_REG);
610                 can_outb(data, address);
611                 can_outb(savewin, base+TS7KV_WIN_REG);
612         }
613         can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
614 }
615
616 /**
617  * tscan1_read_register - Low level read register routine
618  * @address: memory address to read from
619  *
620  * The function tscan1_read_register() is used to read from hardware registers
621  * on the CAN chip. You should only have to edit this function if your hardware
622  * uses some specific read process.
623  * Return Value: The function returns the value stored in @address
624  * File: src/tscan1.c
625  */
626 unsigned tscan1_read_register(can_ioptr_t address)
627 {
628         return can_inb(address);
629 }
630
631 unsigned ts7kv_read_register(can_ioptr_t address)
632 {
633         unsigned long addr=can_ioptr2ulong(address);
634         can_ioptr_t base = can_ulong2ioptr(addr & ~0x1f);
635         unsigned char nwin = 0x10;
636         unsigned char savewin;
637         unsigned val;
638         
639         can_spin_irqflags_t flags;
640
641         if((addr&0x1f) > 0x1d) {
642                 nwin++;
643                 address -= 0x10;
644         }
645
646         can_spin_lock_irqsave(&ts7kv_win_lock,flags);
647         savewin = can_inb(base+TS7KV_WIN_REG);
648         if(nwin == savewin) {
649                 val = can_inb(address);
650         }else{
651                 can_outb(nwin, base+TS7KV_WIN_REG);
652                 val = can_inb(address);
653                 can_outb(savewin, base+TS7KV_WIN_REG);
654         }
655         can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
656
657         return val;
658 }
659
660 int tscan1_register(struct hwspecops_t *hwspecops)
661 {
662         hwspecops->request_io = tscan1_request_io;
663         hwspecops->release_io = tscan1_release_io;
664         hwspecops->reset = tscan1_reset;
665         hwspecops->init_hw_data = tscan1_init_hw_data;
666         hwspecops->init_chip_data = tscan1_init_chip_data;
667         hwspecops->init_obj_data = tscan1_init_obj_data;
668         hwspecops->write_register = tscan1_write_register;
669         hwspecops->read_register = tscan1_read_register;
670         hwspecops->program_irq = tscan1_program_irq;
671         return 0;
672 }
673
674 extern int ts7kv_register(struct hwspecops_t *hwspecops)
675 {
676         hwspecops->request_io = ts7kv_request_io;
677         hwspecops->release_io = ts7kv_release_io;
678         hwspecops->reset = tscan1_reset;
679         hwspecops->init_hw_data = ts7kv_init_hw_data;
680         hwspecops->init_chip_data = ts7kv_init_chip_data;
681         hwspecops->init_obj_data = tscan1_init_obj_data;
682         hwspecops->write_register = ts7kv_write_register;
683         hwspecops->read_register = ts7kv_read_register;
684         hwspecops->program_irq = tscan1_program_irq;
685         return 0;
686 }