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