]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/tscan1.c
The LinCAN driver license unified according to DCE FEE CTU head and superiors request.
[lincan.git] / lincan / src / tscan1.c
1 /**************************************************************************/
2 /* File: tscan1.c - support for TS-CAN1 PC104 card from                   */
3 /*                 Technologic Systems <http://www.embeddedarm.com/>      */
4 /*                                                                        */
5 /* LinCAN - (Not only) Linux CAN bus driver                               */
6 /* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz>   */
7 /* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
8 /* Funded by OCERA and FRESCOR IST projects                               */
9 /* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
10 /*                                                                        */
11 /* LinCAN is free software; you can redistribute it and/or modify it      */
12 /* under terms of the GNU General Public License as published by the      */
13 /* Free Software Foundation; either version 2, or (at your option) any    */
14 /* later version.  LinCAN is distributed in the hope that it will be      */
15 /* useful, but WITHOUT ANY WARRANTY; without even the implied warranty    */
16 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU    */
17 /* General Public License for more details. You should have received a    */
18 /* copy of the GNU General Public License along with LinCAN; see file     */
19 /* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
20 /* Cambridge, MA 02139, USA.                                              */
21 /*                                                                        */
22 /* To allow use of LinCAN in the compact embedded systems firmware        */
23 /* and RT-executives (RTEMS for example), main authors agree with next    */
24 /* special exception:                                                     */
25 /*                                                                        */
26 /* Including LinCAN header files in a file, instantiating LinCAN generics */
27 /* or templates, or linking other files with LinCAN objects to produce    */
28 /* an application image/executable, does not by itself cause the          */
29 /* resulting application image/executable to be covered by                */
30 /* the GNU General Public License.                                        */
31 /* This exception does not however invalidate any other reasons           */
32 /* why the executable file might be covered by the GNU Public License.    */
33 /* Publication of enhanced or derived LinCAN files is required although.  */
34 /**************************************************************************/
35
36
37 #include "../include/can.h"
38 #include "../include/can_sysdep.h"
39 #include "../include/main.h"
40 #include "../include/sja1000p.h"
41
42 #include <linux/module.h>
43 #include "../include/modparms.h"
44 #include "../include/devcommon.h"
45
46 #include "../include/tscan1.h"
47
48 unsigned long tscanio[MAX_HW_CARDS]={-1,-1,-1,-1,-1,-1,-1,-1};
49 unsigned int tscanio_specified;
50
51 #if defined(TSXXX_BASE_IO)
52 unsigned long tsxxx_base=TSXXX_BASE_IO;
53 #else
54 unsigned long tsxxx_base=0;
55 #endif
56
57 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12))
58 MODULE_PARM(tscanio, "1-" __MODULE_STRING(MAX_HW_CARDS)"i");
59 MODULE_PARM(tsxxx_base, "1i");
60 #else /* LINUX_VERSION_CODE >= 2,6,12 */
61 module_param_array(tscanio, ulong, &tscanio_specified, 0);
62 module_param(tsxxx_base, ulong, 0);
63 #endif /* LINUX_VERSION_CODE >= 2,6,12 */
64
65 MODULE_PARM_DESC(tscanio,"TSCAN CAN controller IO address for each board");
66 MODULE_PARM_DESC(tsxxx_base,"The base of the ISA/8-bit IO space for TSxxx CAN peripherals in the system");
67
68 static unsigned long tscan1_tscanio_ranges[] =
69                 { 0x100, 0x120, 0x180, 0x1A0, 0x200, 0x240, 0x280, 0x320};
70
71 static inline unsigned tscan1_read_pld_register(struct candevice_t *candev, can_ioptr_t address)
72 {
73         return candev->hwspecops->read_register(address);
74 }
75
76 static inline void tscan1_write_pld_register(struct candevice_t *candev, unsigned data, can_ioptr_t address)
77 {
78         candev->hwspecops->write_register(data, address);
79 }
80
81 /**
82  * tscan1_request_io: - reserve io or memory range for can board
83  * @candev: pointer to candevice/board which asks for io. Field @io_addr
84  *      of @candev is used in most cases to define start of the range
85  *
86  * The function tscan1_request_io() is used to reserve the io-memory. If your
87  * hardware uses a dedicated memory range as hardware control registers you
88  * will have to add the code to reserve this memory as well.
89  * %IO_RANGE is the io-memory range that gets reserved, please adjust according
90  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
91  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
92  * Return Value: The function returns zero on success or %-ENODEV on failure
93  * File: src/tscan1.c
94  */
95 int tscan1_request_io(struct candevice_t *candev)
96 {
97         unsigned long can_io_addr;
98         unsigned long rebased_can_io_addr;
99         can_ioptr_t remap_can_io_addr;
100         can_ioptr_t remap_pld_io_addr;
101         unsigned char mode;
102         int i, j;
103
104
105         if (!can_request_io_region(candev->io_addr, TSCAN1_IO_RANGE, "tscan1-base")) {
106                 CANMSG("Unable to request base IO port: 0x%lx\n", candev->io_addr);
107                 return -ENODEV;
108         } else {
109                 DEBUGMSG("Registered base IO port: 0x%lx - 0x%lx\n",
110                         candev->io_addr, candev->io_addr+TSCAN1_IO_RANGE-1);
111         }
112
113         can_io_addr = tscanio[candev->candev_idx];
114
115         if(can_io_addr && (can_io_addr != (unsigned long)-1)) {
116                 rebased_can_io_addr = tsxxx_base + can_io_addr;
117
118                 if (!can_request_io_region(rebased_can_io_addr, TSCAN1_CAN_RANGE, "tscan1-can")) {
119                         CANMSG("Unable to request CAN IO port: 0x%lx\n", rebased_can_io_addr);
120                         can_release_io_region(candev->io_addr, TSCAN1_IO_RANGE);
121                         return -ENODEV;
122                 } else {
123                         DEBUGMSG("Registered CAN IO port: 0x%lx - 0x%lx\n",
124                         rebased_can_io_addr, rebased_can_io_addr+TSCAN1_CAN_RANGE-1);
125                 }
126         } else {
127                 for(i = 0; 1; i++) {
128
129                         if(i>=8) {
130                                 CANMSG("Unable find range for CAN IO port\n");
131                                 can_release_io_region(candev->io_addr, TSCAN1_IO_RANGE);
132                                 return -ENODEV;
133                         }
134
135                         can_io_addr = tscan1_tscanio_ranges[i];
136                         for (j = 0; j < MAX_HW_CARDS; j++) {
137                                 if(tscanio[j] == can_io_addr) {
138                                         j = -1;
139                                         break;
140                                 }
141                         }
142                         if(j<0)
143                                 continue;
144
145                         rebased_can_io_addr = tsxxx_base + can_io_addr;
146
147                         if (can_request_io_region(rebased_can_io_addr, TSCAN1_CAN_RANGE, "tscan1-can"))
148                                 break;
149                 }
150
151                 tscanio[candev->candev_idx] = can_io_addr;
152
153                 DEBUGMSG("Found free range and registered CAN IO port: 0x%lx - 0x%lx\n",
154                         rebased_can_io_addr, rebased_can_io_addr+TSCAN1_CAN_RANGE-1);
155         }
156
157         remap_can_io_addr = can_ulong2ioptr(rebased_can_io_addr);
158
159         candev->chip[0]->chip_base_addr = remap_can_io_addr;
160         candev->chip[0]->msgobj[0]->obj_base_addr = remap_can_io_addr;
161
162         mode = 0x60;
163         for(i = 0; i < 8; i++) {
164                 if (can_io_addr == tscan1_tscanio_ranges[i]) {
165                         mode |= i;
166                         break;
167                 }
168         }
169
170         remap_pld_io_addr = can_ulong2ioptr(candev->io_addr);
171
172         tscan1_write_pld_register(candev, 0x00, remap_pld_io_addr+TSCAN1_WIN_REG);
173         tscan1_write_pld_register(candev, mode, remap_pld_io_addr+TSCAN1_MOD_REG);
174
175         return 0;
176 }
177
178 /**
179  * tscan1_release_io - free reserved io memory range
180  * @candev: pointer to candevice/board which releases io
181  *
182  * The function tscan1_release_io() is used to free reserved io-memory.
183  * In case you have reserved more io memory, don't forget to free it here.
184  * IO_RANGE is the io-memory range that gets released, please adjust according
185  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
186  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
187  * Return Value: The function always returns zero
188  * File: src/tscan1.c
189  */
190 int tscan1_release_io(struct candevice_t *candev)
191 {
192         unsigned long rebased_can_io_addr;
193         can_ioptr_t remap_pld_io_addr = can_ulong2ioptr(candev->io_addr);
194
195         if(candev->chip[0]){
196                 rebased_can_io_addr = candev->chip[0]->chip_base_addr;
197                 if(rebased_can_io_addr != (unsigned long)-1)
198                         can_release_io_region(rebased_can_io_addr, TSCAN1_CAN_RANGE);
199         }
200
201         tscan1_write_pld_register(candev, 0x20, remap_pld_io_addr+TSCAN1_MOD_REG);
202
203         can_release_io_region(candev->io_addr, TSCAN1_IO_RANGE);
204         return 0;
205 }
206
207 /**
208  * tscan1_reset - hardware reset routine
209  * @candev: Pointer to candevice/board structure
210  *
211  * The function tscan1_reset() is used to give a hardware reset. This is
212  * rather hardware specific so I haven't included example code. Don't forget to
213  * check the reset status of the chip before returning.
214  * Return Value: The function returns zero on success or %-ENODEV on failure
215  * File: src/tscan1.c
216  */
217 int tscan1_reset(struct candevice_t *candev)
218 {
219         unsigned short i=0, chipnr;
220         struct canchip_t *chip;
221
222         DEBUGMSG("Resetting %s hardware ...\n", candev->hwname);
223
224         for(chipnr=0;chipnr<candev->nr_sja1000_chips;chipnr++) {
225                 chip=candev->chip[chipnr];
226                 can_write_reg(chip, sjaMOD_RM, SJAMOD);
227                 udelay(1000);
228                 can_write_reg(chip, 0x00, SJAIER);
229                 udelay(1000);
230                 i=20;
231                 while (can_read_reg(chip, SJAMOD)&sjaMOD_RM){
232                         if(!i--) return -ENODEV;
233                         udelay(1000);
234                         can_write_reg(chip, 0, SJAMOD);
235                 }
236                 udelay(1000);
237                 can_write_reg(chip, sjaCDR_PELICAN, SJACDR);
238                 can_write_reg(chip, 0x00, SJAIER);
239         }
240
241         return 0;
242 }
243
244 #define RESET_ADDR 0x100
245 #define NR_82527 0
246 #define NR_SJA1000 1
247
248 int tscan1_check_presence(struct candevice_t *candev, unsigned long rebased_pld_io_addr, int *pjmp)
249 {
250         can_ioptr_t remap_pld_io_addr;
251         int result = -ENODEV;
252
253         if (!can_request_io_region(rebased_pld_io_addr, TSCAN1_IO_RANGE, "tscan1-probe"))
254                 return -ENODEV;
255
256         do {
257                 remap_pld_io_addr = can_ulong2ioptr(rebased_pld_io_addr);
258
259                 if (tscan1_read_pld_register(candev, remap_pld_io_addr+TSCAN1_ID0_REG)!=TSCAN1_ID0 ||
260                         tscan1_read_pld_register(candev, remap_pld_io_addr+TSCAN1_ID1_REG)!=TSCAN1_ID1)
261                         break;
262
263                 tscan1_write_pld_register(candev, 0x00, remap_pld_io_addr+TSCAN1_WIN_REG);
264                 tscan1_write_pld_register(candev, 0x20, remap_pld_io_addr+TSCAN1_MOD_REG);
265
266                 if(pjmp)
267                         *pjmp = tscan1_read_pld_register(candev, remap_pld_io_addr+TSCAN1_JMP_REG);
268
269                 result = 0;
270         } while (0);
271
272         can_release_io_region(rebased_pld_io_addr, TSCAN1_IO_RANGE);
273
274         return result;
275 }
276
277
278 /**
279  * tscan1_init_hw_data - Initialize hardware cards
280  * @candev: Pointer to candevice/board structure
281  *
282  * The function tscan1_init_hw_data() is used to initialize the hardware
283  * structure containing information about the installed CAN-board.
284  * %RESET_ADDR represents the io-address of the hardware reset register.
285  * %NR_82527 represents the number of intel 82527 chips on the board.
286  * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
287  * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
288  * the hardware uses programmable interrupts.
289  * Return Value: The function always returns zero
290  * File: src/tscan1.c
291  */
292
293 int tscan1_init_hw_data(struct candevice_t *candev)
294 {
295         int i, j, jmp;
296         unsigned long io_addr;
297         unsigned long rebased_pld_io_addr;
298
299         io_addr = candev->io_addr;
300
301         if(io_addr && (io_addr != (unsigned long)-1)) {
302                 rebased_pld_io_addr = io_addr + tsxxx_base;
303
304                 if(tscan1_check_presence(candev, rebased_pld_io_addr, &jmp)){
305                         CANMSG("No TSCAN1 card found at address 0x%lx\n",
306                                 rebased_pld_io_addr);
307                         return -ENODEV;
308                 }
309         } else {
310                 DEBUGMSG("Scanning bus for TS-CAN1 boards...\n");
311
312                 for (i=0; 1;i++)
313                 {
314                         if(i >= 4) {
315                                 CANMSG("No TS-CAN1 boards found for slot %d\n", candev->candev_idx);
316                                 return -ENODEV;
317                         }
318
319                         io_addr = TSCAN1_BASE_IO + i*TSCAN1_IO_RANGE;
320                         rebased_pld_io_addr = io_addr + tsxxx_base;
321
322                         for (j = 0; j < MAX_HW_CARDS; j++) {
323                                 if(io[j] == io_addr){
324                                         j = -1;
325                                         break;
326                                 }
327                         }
328                         if(j<0)
329                                 continue;
330
331                         if(!tscan1_check_presence(candev, rebased_pld_io_addr, &jmp))
332                                 break;
333
334                 }
335                 DEBUGMSG("TS-CAN1 board was found at 0x%lx for driver slot %d\n",
336                                         io_addr, candev->candev_idx);
337
338                 io[candev->candev_idx] = io_addr;
339         }
340
341         candev->io_addr = rebased_pld_io_addr;
342         /* unused reset address is used to store jumper setting */
343         candev->res_addr = jmp;
344
345         candev->nr_82527_chips=NR_82527;
346         candev->nr_sja1000_chips=NR_SJA1000;
347         candev->nr_all_chips=NR_82527+NR_SJA1000;
348         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
349
350         DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
351                 candev->io_addr, candev->candev_idx, candev->hwname);
352
353         return 0;
354 }
355
356
357 /**
358  * tscan1_init_chip_data - Initialize chips
359  * @candev: Pointer to candevice/board structure
360  * @chipnr: Number of the CAN chip on the hardware card
361  *
362  * The function tscan1_init_chip_data() is used to initialize the hardware
363  * structure containing information about the CAN chips.
364  * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
365  * "sja1000".
366  * The @chip_base_addr entry represents the start of the 'official' memory map
367  * of the installed chip. It's likely that this is the same as the @io_addr
368  * argument supplied at module loading time.
369  * The @clock entry holds the chip clock value in Hz.
370  * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
371  * register. Options defined in the %sja1000.h file:
372  * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
373  * The entry @sja_ocr_reg holds hardware specific options for the Output Control
374  * register. Options defined in the %sja1000.h file:
375  * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
376  * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
377  * The entry @int_clk_reg holds hardware specific options for the Clock Out
378  * register. Options defined in the %i82527.h file:
379  * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
380  * The entry @int_bus_reg holds hardware specific options for the Bus
381  * Configuration register. Options defined in the %i82527.h file:
382  * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
383  * Return Value: The function always returns zero
384  * File: src/tscan1.c
385  */
386 int tscan1_init_chip_data(struct candevice_t *candev, int chipnr)
387 {
388         unsigned long default_clk = 16000 * 1000;
389         int jmp;
390         int irq = -1;
391
392         /* unused reset address is used to store jumper setting */
393         jmp = candev->res_addr;
394
395         if (jmp&0x10 && jmp&0x20) irq=TSXXX_IRQ5;
396         else if (jmp&0x10) irq=TSXXX_IRQ6;
397         else if (jmp&0x20) irq=TSXXX_IRQ7;
398
399         if(irq<0) {
400                 CANMSG("Jumpers select no IRQ for TSCAN1 at 0x%lx of driver %d/%s\n",
401                         candev->io_addr, candev->candev_idx, candev->hwname);
402                 return -ENODEV;
403         }
404         candev->chip[chipnr]->chip_irq = irq;
405
406         sja1000p_fill_chipspecops(candev->chip[chipnr]);
407
408         if(candev->chip[chipnr]->clock <= 0)
409                 candev->chip[chipnr]->clock = default_clk;
410         candev->chip[chipnr]->int_clk_reg = 0x0;
411         candev->chip[chipnr]->int_bus_reg = 0x0;
412         candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
413         candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
414         /* 
415          * The address is assigned during tscan1_request_io()
416          * according to found free ranges or tscanio option
417          */
418         candev->chip[chipnr]->chip_base_addr = (unsigned long)-1;
419
420         return 0;
421 }
422
423 /**
424  * tscan1_init_obj_data - Initialize message buffers
425  * @chip: Pointer to chip specific structure
426  * @objnr: Number of the message buffer
427  *
428  * The function tscan1_init_obj_data() is used to initialize the hardware
429  * structure containing information about the different message objects on the
430  * CAN chip. In case of the sja1000 there's only one message object but on the
431  * i82527 chip there are 15.
432  * The code below is for a i82527 chip and initializes the object base addresses
433  * The entry @obj_base_addr represents the first memory address of the message
434  * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
435  * base address.
436  * Unless the hardware uses a segmented memory map, flags can be set zero.
437  * Return Value: The function always returns zero
438  * File: src/tscan1.c
439  */
440 int tscan1_init_obj_data(struct canchip_t *chip, int objnr)
441 {
442         chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
443         return 0;
444 }
445
446 /**
447  * tscan1_program_irq - program interrupts
448  * @candev: Pointer to candevice/board structure
449  *
450  * The function tscan1_program_irq() is used for hardware that uses
451  * programmable interrupts. If your hardware doesn't use programmable interrupts
452  * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
453  * leave this function unedited. Again this function is hardware specific so
454  * there's no example code.
455  * Return value: The function returns zero on success or %-ENODEV on failure
456  * File: src/tscan1.c
457  */
458 int tscan1_program_irq(struct candevice_t *candev)
459 {
460         return 0;
461 }
462
463 /**
464  * tscan1_write_register - Low level write register routine
465  * @data: data to be written
466  * @address: memory address to write to
467  *
468  * The function tscan1_write_register() is used to write to hardware registers
469  * on the CAN chip. You should only have to edit this function if your hardware
470  * uses some specific write process.
471  * Return Value: The function does not return a value
472  * File: src/tscan1.c
473  */
474 void tscan1_write_register(unsigned data, can_ioptr_t address)
475 {
476         can_outb(data, address);
477 }
478
479 void tscan1mmio_write_register(unsigned data, can_ioptr_t address)
480 {
481         can_writeb(data, address);
482 }
483
484
485 /**
486  * tscan1_read_register - Low level read register routine
487  * @address: memory address to read from
488  *
489  * The function tscan1_read_register() is used to read from hardware registers
490  * on the CAN chip. You should only have to edit this function if your hardware
491  * uses some specific read process.
492  * Return Value: The function returns the value stored in @address
493  * File: src/tscan1.c
494  */
495 unsigned tscan1_read_register(can_ioptr_t address)
496 {
497         return can_inb(address);
498 }
499
500 unsigned tscan1mmio_read_register(can_ioptr_t address)
501 {
502         return can_readb(address);
503 }
504
505 int tscan1_register(struct hwspecops_t *hwspecops)
506 {
507         hwspecops->request_io = tscan1_request_io;
508         hwspecops->release_io = tscan1_release_io;
509         hwspecops->reset = tscan1_reset;
510         hwspecops->init_hw_data = tscan1_init_hw_data;
511         hwspecops->init_chip_data = tscan1_init_chip_data;
512         hwspecops->init_obj_data = tscan1_init_obj_data;
513         hwspecops->write_register = tscan1_write_register;
514         hwspecops->read_register = tscan1_read_register;
515         hwspecops->program_irq = tscan1_program_irq;
516         return 0;
517 }
518
519 int tscan1mmio_register(struct hwspecops_t *hwspecops)
520 {
521         hwspecops->request_io = tscan1_request_io;
522         hwspecops->release_io = tscan1_release_io;
523         hwspecops->reset = tscan1_reset;
524         hwspecops->init_hw_data = tscan1_init_hw_data;
525         hwspecops->init_chip_data = tscan1_init_chip_data;
526         hwspecops->init_obj_data = tscan1_init_obj_data;
527         hwspecops->write_register = tscan1mmio_write_register;
528         hwspecops->read_register = tscan1mmio_read_register;
529         hwspecops->program_irq = tscan1_program_irq;
530         return 0;
531 }