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