1 /**************************************************************************/
2 /* File: ts7kv.c - support for TS-7KV multifunction card from */
3 /* Technologic Systems <http://www.embeddedarm.com/> */
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> */
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. */
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: */
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 /**************************************************************************/
37 #include "../include/can.h"
38 #include "../include/can_sysdep.h"
39 #include "../include/main.h"
40 #include "../include/sja1000p.h"
42 #include <linux/module.h>
43 #include "../include/modparms.h"
44 #include "../include/devcommon.h"
46 #include "../include/ts7kv.h"
48 static CAN_DEFINE_SPINLOCK(ts7kv_win_lock);
50 #ifdef CONFIG_OC_LINCAN_CARD_tscan1
52 extern unsigned long tsxxx_base;
54 #else /*CONFIG_OC_LINCAN_CARD_tscan1*/
56 #if defined(TSXXX_BASE_IO)
57 unsigned long tsxxx_base=TSXXX_BASE_IO;
59 unsigned long tsxxx_base=0;
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 */
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");
71 #endif /*CONFIG_OC_LINCAN_CARD_tscan1*/
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
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
87 int ts7kv_request_io(struct candevice_t *candev)
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);
93 DEBUGMSG("Registered CAN IO port: 0x%lx - 0x%lx\n",
94 candev->io_addr, candev->io_addr+TS7KV_CAN_RANGE-1);
101 * ts7kv_release_io - free reserved io memory range
102 * @candev: pointer to candevice/board which releases io
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
112 int ts7kv_release_io(struct candevice_t *candev)
114 can_release_io_region(candev->io_addr, TS7KV_CAN_RANGE);
119 * ts7kv_reset - hardware reset routine
120 * @candev: Pointer to candevice/board structure
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
128 int ts7kv_reset(struct candevice_t *candev)
130 unsigned short i=0, chipnr;
131 struct canchip_t *chip;
133 DEBUGMSG("Resetting %s hardware ...\n", candev->hwname);
135 for(chipnr=0;chipnr<candev->nr_sja1000_chips;chipnr++) {
136 chip=candev->chip[chipnr];
137 can_write_reg(chip, sjaMOD_RM, SJAMOD);
139 can_write_reg(chip, 0x00, SJAIER);
142 while (can_read_reg(chip, SJAMOD)&sjaMOD_RM){
143 if(!i--) return -ENODEV;
145 can_write_reg(chip, 0, SJAMOD);
148 can_write_reg(chip, sjaCDR_PELICAN, SJACDR);
149 can_write_reg(chip, 0x00, SJAIER);
155 int ts7kv_check_presence(unsigned long remap_io_addr, int *pjmp)
157 int result = -ENODEV;
159 if (!can_request_io_region(remap_io_addr, TS7KV_IO_RANGE, "ts7kv-probe"))
163 if (can_inb(remap_io_addr+TS7KV_ID0_REG)!=TS7KV_ID0 ||
164 can_inb(remap_io_addr+TS7KV_ID1_REG)!=TS7KV_ID1)
168 *pjmp = can_inb(remap_io_addr+TS7KV_JMP_REG);
173 can_release_io_region(remap_io_addr, TS7KV_IO_RANGE);
178 #define RESET_ADDR 0x100
183 * ts7kv_init_hw_data - Initialize hardware cards
184 * @candev: Pointer to candevice/board structure
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
196 int ts7kv_init_hw_data(struct candevice_t *candev)
199 unsigned long io_addr;
200 unsigned long remap_io_addr;
201 unsigned long can_io_addr;
203 io_addr = candev->io_addr;
205 if(io_addr && (io_addr != (unsigned long)-1)) {
206 remap_io_addr = io_addr + tsxxx_base;
208 if(ts7kv_check_presence(remap_io_addr, &jmp)){
209 CANMSG("No TS7KV card found at address 0x%lx\n",
214 DEBUGMSG("Scanning bus for TS7KV boards...\n");
219 CANMSG("No TS7KV boards found for slot %d\n", candev->candev_idx);
223 io_addr = TS7KV_BASE_IO + i*TS7KV_IO_RANGE;
224 remap_io_addr = io_addr + tsxxx_base;
226 for (j = 0; j < MAX_HW_CARDS; j++) {
227 if(io[j] == io_addr){
235 if(!ts7kv_check_presence(remap_io_addr, &jmp))
239 DEBUGMSG("TS7KV board was found at 0x%lx for driver slot %d\n",
240 io_addr, candev->candev_idx);
242 io[candev->candev_idx] = io_addr;
245 can_io_addr = ((io_addr>>3)&0x03)*0x20;
247 /* dev_base_addr address is used to store remapped PLD base address */
248 candev->dev_base_addr = remap_io_addr;
250 /* dev_base_addr address is used to store remapped slave window address */
251 candev->io_addr = can_io_addr+tsxxx_base;
253 /* unused reset address is used to store jumper setting */
254 candev->res_addr = jmp;
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;
261 DEBUGMSG("Memory region at 0x%lx assigned to sja1000 of driver %d/%s\n",
262 candev->io_addr, candev->candev_idx, candev->hwname);
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
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
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
296 int ts7kv_init_chip_data(struct candevice_t *candev, int chipnr)
298 unsigned long default_clk = 16000 * 1000;
302 /* unused reset address is used to store jumper setting */
303 jmp = candev->res_addr;
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;
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);
315 candev->chip[chipnr]->chip_irq = irq;
317 sja1000p_fill_chipspecops(candev->chip[chipnr]);
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;
331 * ts7kv_init_obj_data - Initialize message buffers
332 * @chip: Pointer to chip specific structure
333 * @objnr: Number of the message buffer
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
343 * Unless the hardware uses a segmented memory map, flags can be set zero.
344 * Return Value: The function always returns zero
347 int ts7kv_init_obj_data(struct canchip_t *chip, int objnr)
349 chip->msgobj[objnr]->obj_base_addr = chip->chip_base_addr;
354 * ts7kv_program_irq - program interrupts
355 * @candev: Pointer to candevice/board structure
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
365 int ts7kv_program_irq(struct candevice_t *candev)
371 * ts7kv_write_register - Low level write register routine
372 * @data: data to be written
373 * @address: memory address to write to
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
381 void ts7kv_write_register(unsigned data, can_ioptr_t address)
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;
388 can_spin_irqflags_t flags;
390 if((addr&0x1f) > 0x1d) {
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);
400 can_outb(nwin, base+TS7KV_WIN_REG);
401 can_outb(data, address);
402 can_outb(savewin, base+TS7KV_WIN_REG);
404 can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
408 * ts7kv_read_register - Low level read register routine
409 * @address: memory address to read from
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
417 unsigned ts7kv_read_register(can_ioptr_t address)
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;
425 can_spin_irqflags_t flags;
427 if((addr&0x1f) > 0x1d) {
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);
437 can_outb(nwin, base+TS7KV_WIN_REG);
438 val = can_inb(address);
439 can_outb(savewin, base+TS7KV_WIN_REG);
441 can_spin_unlock_irqrestore(&ts7kv_win_lock,flags);
446 extern int ts7kv_register(struct hwspecops_t *hwspecops)
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;