www.pudn.com > canbus4linux.rar > isa_sja1000.c
/* * isa_sja1000.c * Copyright (c) 2003 Kirill Smelkov* * ... BLURB ABOUT HARDWARE... * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * two words about prefixes: * board_ prefix marks routines assotiated with the whole board * isa_sja1000_ prefix marks routines assotiated with some chip on the board */ #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,67) #include #endif #include #include "trace.h" #include "canbus4linux.h" #include "isa_sja1000.h" #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif struct io_info { unsigned long io_base; unsigned long io_len; }; struct board_info { const char *name; int io_irq; // irq line long Fosc; // oscillator frequency u8 OCR; // OCR (output control register) value specific for board int nchips; struct io_info io_info[MAXCHIPS]; }; static const struct board_info boards_list[] = { // name irq Fosc, OCR, nchips {io_base, io_len} for each chip { "can-200pc", 5, 16000000, 0x1b, 2, { {0x100, 0x20}, {0x120, 0x20} } }, // next two are for testing { "c200-1", 5, 16000000, 0x1b, 1, { {0x100, 0x20} } }, // first channel of can-200pc { "c200-2", 5, 16000000, 0x1b, 1, { {0x120, 0x20} } }, // second channel of can-200pc { NULL } }; // find specified board in the list static int find_board(const char * name) { int i=0; while (boards_list[i].name) { if ( !strcmp(name, boards_list[i].name) ) return i; ++i; } return -1; } #define MAXBOARDS 2 static ISA_SJA1000_BOARD devices[MAXBOARDS]; // ******************* // * REGISTER ACCESS * // ******************* static void isa_sja1000_writereg(void * data, u8 reg, u8 val) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; TRACE("writereg(0x%2.2x,0x%2.2x)", reg, val); outb(val, self->io_base + reg); } static u8 isa_sja1000_readreg(void * data, u8 reg) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; u8 val; val = inb(self->io_base + reg); TRACE("readreg(0x%2.2x) = 0x%2.2x", reg, val); return val; } // *** ?unneeded? _reset routines... /* static inline void isa_sja1000_reset_mode(ISA_SJA1000_CHIP * self) { u8 val = inb(self->io_base + 0); u8 CR = val | 0x01; int tries=100; while ( !(val & 0x01)) { outb_p(CR, self->io_base + 0); val = inb(self->io_base + 0); if (--tries == 0) { TRACE("can't reset sja1000... XXX: ?what can i do here?"); break; } }; } static inline void isa_sja1000_operating_mode(ISA_SJA1000_CHIP * self) { u8 val = inb(self->io_base + 0); u8 CR = val & 0x3e; // ? 1e int tries=100; while (val & 0x01) { outb_p(CR, self->io_base + 0); val = inb(self->io_base + 0); if (--tries == 0) { TRACE("can't put sja1000 into operating mode... XXX: ?what can i do here?"); break; } } } static void isa_sja1000_writereg_reset(void * data, u8 reg, u8 val) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; TRACE("DEPRECATED!"); TRACE("writereg_reset(0x%2.2x,0x%2.2x)", reg, val); isa_sja1000_reset_mode(self); outb(val, self->io_base + reg); isa_sja1000_operating_mode(self); } static u8 isa_sja1000_readreg_reset(void * data, u8 reg) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; u8 ret; isa_sja1000_reset_mode(self); ret = inb(self->io_base + reg); isa_sja1000_operating_mode(self); TRACE("DEPRECATED!"); TRACE("readreg_reset(0x%2.2x) = 0x%2.2x", reg, ret); return ret; } */ // **************** // * IRQ HANDLING * // **************** static int isa_sja1000_register_isr(void * data, sja1000_isr chip_isr, struct sja1000_admin * chip_isr_data) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; TRACE("register_isr()"); if (!self) return -EINVAL; self->chip_isr = chip_isr; self->chip_isr_data = chip_isr_data; return 0; } static int isa_sja1000_unregister_isr(void * data) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; TRACE("unregister_isr()"); if (!self) return -EINVAL; self->chip_isr = 0; self->chip_isr_data = 0; return 0; } // the whole board interrupt service routine #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4) static irqreturn_t board_isr(int irq, void * dev_id, struct pt_regs * regs) #else static void board_isr(int irq, void * dev_id, struct pt_regs * regs) #endif { ISA_SJA1000_BOARD * self = (ISA_SJA1000_BOARD *) dev_id; unsigned int i; TRACE("isr()"); if (!self) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4) return IRQ_NONE; #else return; #endif for (i=0;i nchips;++i) if (self->chips[i].chip_isr) self->chips[i].chip_isr(&self->chips[i],self->chips[i].chip_isr_data); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4) return IRQ_HANDLED; #else return; #endif } // ***************** // * OPEN & CLOSE * // ***************** static int isa_sja1000_open(void * data) { ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; int err; TRACE("open()"); // spin_lock(&self->lock); do { if (self->open) { err = -EBUSY; break; } self->open++; // NOTE: this is currently specific for my card (the wierd thing) // hardware reset is made via isa_sja1000_writereg(self, 0x1e, 0x00); // reset } while (0); // spin_unlock(&self->lock); return 0; } static int isa_sja1000_close(void * data) { // ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data; TRACE("close()"); return 0; } char * cards[MAXBOARDS]; MODULE_PARM(cards,"1-" __MODULE_STRING(MAXBOARDS) "s"); MODULE_PARM_DESC(cards, "specify (comma separated) CAN board models"); // ************************* // * MODULE INIT & CLEANUP * // ************************* void __exit isa_sja1000_cleanup(void); int __init isa_sja1000_init(void) { int err=-ENODEV; int num, i, minor; int cards_idx[MAXBOARDS]; TRACE("init()"); if (!cards[0]) { TRACE("you have to specify your card (cards parameter). giving up."); return -ENODEV; } for (num=0;num Fosc, output_control_register: idev->OCR }; // io for all chips an the board TRACE("requesting io..."); for (i=0;i nchips;++i) { ISA_SJA1000_CHIP * chip = &dev->chips[i]; unsigned long io_base; unsigned long io_len; snprintf(chip->name, MAX_DEVICE_NAME_LENGTH, "%s[%i]", idev->name, i); chip->num = -1; chip->lock = SPIN_LOCK_UNLOCKED; dev->nchips++; io_base = idev->io_info[i].io_base; io_len = idev->io_info[i].io_len; if (!request_region(io_base, io_len, chip->name)) { TRACE("request_region(%lx,%lx) failed", io_base, io_len); err=-EBUSY; goto out; } chip->io_base = io_base; chip->io_len = io_len; ++minor; } // irq for the board io_irq = idev->io_irq; TRACE("requesting irq..."); err = request_irq(io_irq, board_isr, 0 /* flags */, "isacan", dev); if (err) { TRACE("request_irq(%i) failed", io_irq); goto out; } dev->io_irq = io_irq; // register every chip on the board in the canbus4linux subsystem for (i=0;i nchips;++i) { ISA_SJA1000_CHIP * chip = &dev->chips[i]; TRACE("%s: registering device no.: %i ...", idev->name, i); chip->num = sja1000_register_device(chip->name, CANBUS4LINUX_VERSION, chip, &access, 0, minor); if (chip->num == -1) { TRACE("sja1000_register_device() failed"); err = -ENOMEM; goto out; // not enough memory to register this device } } err = 0; } out: if (err) isa_sja1000_cleanup(); return err; } void __exit isa_sja1000_cleanup(void) { int num, i; TRACE("cleanup()"); for (num=0;num nchips) continue; for (i=0; i nchips; ++i) { ISA_SJA1000_CHIP * chip = &dev->chips[i]; if (chip->num!=-1) chip->num = sja1000_unregister_device(chip->num); } if (dev->io_irq) free_irq(dev->io_irq, dev); for (i=0; i nchips; ++i) { ISA_SJA1000_CHIP * chip = &dev->chips[i]; if (chip->io_base) release_region(chip->io_base, chip->io_len); } } } module_init(isa_sja1000_init); module_exit(isa_sja1000_cleanup); MODULE_AUTHOR("Kirill Smelkov "); MODULE_DESCRIPTION("CAN driver for generic SJA1000 based ISA card"); // vim: ts=4