📄 pcan_pccard_kernel.c
字号:
//****************************************************************************// Copyright (C) 2006-2007 PEAK System-Technik GmbH//// linux@peak-system.com// www.peak-system.com//// 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., 675 Mass Ave, Cambridge, MA 02139, USA.//// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)//// Major contributions by:// Edouard Tisserant (edouard.tisserant@lolitech.fr) XENOMAI// Laurent Bessard (laurent.bessard@lolitech.fr) XENOMAI// Oliver Hartkopp (oliver.hartkopp@volkswagen.de) socketCAN// //****************************************************************************//***************************************************************************//// all parts to handle the interface specific parts of pcan-pccard//// $Id: pcan_pccard_kernel.c 541 2008-02-18 17:48:03Z edouard $////****************************************************************************//****************************************************************************// INCLUDES#include <src/pcan_common.h> // must always be the 1st include#include <linux/errno.h>#include <linux/sched.h>#include <linux/delay.h>#include <asm/io.h>#include <linux/types.h>#include <linux/timer.h> // to support activity scan#include <linux/slab.h>#include <src/pcan_main.h>#include <src/pcan_pccard.h>#include <src/pcan_sja1000.h>#include <src/pcan_fifo.h>#ifdef NETDEV_SUPPORT#include <src/pcan_netdev.h> // for hotplug pcan_netdev_register()#endif#include <src/pcan_pccard_kernel.h>#include <src/pcan_filter.h>//****************************************************************************// DEFINES#define PCCARD_PORT_SIZE 0x20 // range of a channels port#define PCCARD_COMMON_SIZE 0x40 // range of channels common ports#define PCCARD_CHANNEL_OFF 0x20 // port offset of sja1000 channel #2#define PCCARD_COMMON 0x40 // port offset of common area // offsets from base of common area#define CCR 0x00 // CAN control register#define CSR 0x02 // CAN status register#define CPR 0x04 // CAN power register#define SPIDATI 0x06 // SPI data in#define SPIDATO 0x08 // SPI data out#define SPIADR 0x0A // SPI address register#define SPIINS 0x0C // SPI instruction register#define FW_MAJOR 0x10 // firmware major number (local)#define FW_MINOR 0x12 // firmware minor number (local)#define CCR_CLK_MASK 0x03 // mask for clock code#define CCR_CLK16 0x00 // clk code#define CCR_CLK10 0x01 // #define CCR_CLK21 0x02 // #define CCR_CLK8 0x03 // #define CCR_CLK_DEFAULT CCR_CLK16 // set default clock #define CCR_RESET_MASK 0x01#define CCR_RESET 0x01 // put channel into reset#define CCR_RESET0_SHIFT 2 // shift for channel 0#define CCR_RESET1_SHIFT 3 // shift for channel 1#define CCR_LED_MASK 0x03 // mask for LED channel 0 code#define CCR_LED_ON 0x00 // switch LED permanently on#define CCR_LED_FAST 0x01 // switch LED to 4 Hz flash#define CCR_LED_SLOW 0x02 // switch LED to 1 Hz flash#define CCR_LED_OFF 0x03 // switch LED off#define CCR_LED0_SHIFT 4 // shift for channel 1#define CCR_LED1_SHIFT 6 // shift for channel 1#define CCR_DEFAULT ((((u8)CCR_LED_OFF) << CCR_LED0_SHIFT) | (((u8)CCR_LED_OFF) << CCR_LED1_SHIFT) | CCR_CLK_DEFAULT) #define CSR_SPIBUSY 0x04 // set when SPI transaction is busy#define SPI_MAX_WAIT_CYCLES 100#define MAX_LOOP_CYCLES 1000 // cycles to wait if card is plugged out or damaged//****************************************************************************// helper macros//****************************************************************************// GLOBALS//****************************************************************************// LOCALSstatic u16 pccard_devices = 0;//****************************************************************************// CODE// it seems that even when called after 'pccardctl eject' the card is seen as// already plugged out. So this function allways return 0.// BTW this does not matter since PCCARDs power is switched off during// plug out or 'pccardctl eject'. Therefore no de-initialisation of hardware // components are necessary.static inline int pccard_plugged(PCAN_PCCARD *card){ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) return (pcmcia_dev_present(card->pcc_dev) != NULL); #else #if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,16) return DEV_OK(card->link.handle); #else return DEV_OK((&card->link)); #endif #endif}//****************************************************************************// read channel individual registerstatic u8 pccard_readreg(struct pcandev *dev, u8 port) // read a register{ return inb(dev->port.pccard.dwPort + port);}// write channel individual registerstatic void pccard_writereg(struct pcandev *dev, u8 port, u8 data) // write a register{ outb(data, dev->port.pccard.dwPort + port) ; }// read common register to both channels of a cardstatic inline u8 pccard_readreg_common(PCAN_PCCARD *card, u8 port) // read a register{ return inb(card->commonPort + port);}// write common register to both channels of a cardstatic inline void pccard_writereg_common(PCAN_PCCARD *card, u8 port, u8 data) // write a register{ outb(data, card->commonPort + port); }//****************************************************************************// SPI enginestatic int wait_while_spi_engine_busy(PCAN_PCCARD *card){ int counter = SPI_MAX_WAIT_CYCLES; do schedule(); while ((pccard_readreg_common(card, CSR) & CSR_SPIBUSY) && counter--); if (counter <= 0) return -EIO; else return 0;}static int pccard_write_eeprom(PCAN_PCCARD *card, u16 addr, u8 val){ u16 status; int err = 0; int counter; DPRINTK(KERN_DEBUG "%s: pccard_write_eeprom()\n", DEVICE_NAME); // Instruction WRITE pccard_writereg_common(card, SPIINS, 0x06); // WREN if ((err = wait_while_spi_engine_busy(card)) < 0) goto fail; // warte, bis WEL gesetzt ist counter = MAX_LOOP_CYCLES; do { pccard_writereg_common(card, SPIINS, 0x05); // RDSR == Read status if ((err = wait_while_spi_engine_busy(card)) < 0) goto fail; status = pccard_readreg_common(card, SPIDATI); } while ((!(status & 0x02)) && counter--); // warte, bis WEL (write enable) 1. if (counter <= 0) { err = -EIO; goto fail; } // Adresse und Data setzen pccard_writereg_common(card, SPIADR, addr & 0xff); pccard_writereg_common(card, SPIDATO, val); // Instruction WRITE pccard_writereg_common(card, SPIINS, ((addr & 0x100) ? 8 : 0) | 0x02); // WRITE mit bit3 = Addr8 if ((err = wait_while_spi_engine_busy(card)) < 0) goto fail; // warte, bis Schreiben abgeschlossen counter = MAX_LOOP_CYCLES; do { pccard_writereg_common(card, SPIINS, 0x05); // RDSR == Read status if ((err = wait_while_spi_engine_busy(card)) < 0) goto fail; status = pccard_readreg_common(card, SPIDATI); } while ((status & 0x01) && counter--); // warte, bis WIP (Write In Progress) 0. if (counter <= 0) err = -EIO; fail: if (err) { DPRINTK(KERN_DEBUG "%s: pccard_write_eeprom(0x%04x, 0x%02x) failed!\n", DEVICE_NAME, addr, val); } return err;}//****************************************************************************// helper functions// get firmware number of PCCstatic inline void pccard_show_firmware_version(PCAN_PCCARD *card){ u8 fw_major = pccard_readreg_common(card, FW_MAJOR); u8 fw_minor = pccard_readreg_common(card, FW_MINOR); printk(KERN_INFO "%s: pccard firmware %d.%d\n", DEVICE_NAME, fw_major, fw_minor);}// init CCR settingsstatic inline void pccard_initreg_common(PCAN_PCCARD *card){ pccard_writereg_common(card, CCR, CCR_DEFAULT); }// hard reset only one channel to its default settingsstatic inline void pccard_channel_reset(PCAN_PCCARD *card, int nChannel){ u8 data; u8 shift = (nChannel) ? CCR_RESET1_SHIFT : CCR_RESET0_SHIFT; DPRINTK(KERN_DEBUG "%s: pccard_channel_reset(%d)\n", DEVICE_NAME, nChannel); data = pccard_readreg_common(card, CCR); data &= ~(CCR_RESET_MASK << shift); data |= (CCR_RESET << shift); pccard_writereg_common(card, CCR, data); mdelay(2); data = pccard_readreg_common(card, CCR); data &= ~(CCR_RESET_MASK << shift); data &= ~(CCR_RESET << shift); pccard_writereg_common(card, CCR, data); mdelay(10); // wait until reset has settled}// set LEDstatic inline void pccard_set_LED(PCAN_PCCARD *card, int nChannel, u8 mode){ u8 data; u8 shift = (nChannel) ? CCR_LED1_SHIFT : CCR_LED0_SHIFT; mode &= CCR_LED_MASK; data = pccard_readreg_common(card, CCR); // write only if something has changed if (((data >> shift) & CCR_LED_MASK) != mode) { data &= ~(CCR_LED_MASK << shift); data |= (mode << shift); pccard_writereg_common(card, CCR, data); }}// enable CAN powerstatic inline void pccard_enable_CAN_power(PCAN_PCCARD *card){ DPRINTK(KERN_DEBUG "%s: pccard_enable_CAN_power()\n", DEVICE_NAME); pccard_write_eeprom(card, 0, 1);}// disable CAN powerstatic inline void pccard_disable_CAN_power(PCAN_PCCARD *card){ DPRINTK(KERN_DEBUG "%s: pccard_disable_CAN_power()\n", DEVICE_NAME); pccard_write_eeprom(card, 0, 0);}//****************************************************************************// activity scanner to control LEDsstatic void pccard_activity_scanner(unsigned long ptr){ PCAN_PCCARD *card = (PCAN_PCCARD *)ptr; struct pcandev *dev; int i; // DPRINTK(KERN_DEBUG "%s: pccard_activity_scanner(%p)\n", DEVICE_NAME, card); for (i = 0; i < PCCARD_CHANNELS; i++) { dev = card->dev[i]; if (dev) { u8 state = dev->ucActivityState; switch(state) { case ACTIVITY_XMIT: dev->ucActivityState = ACTIVITY_IDLE; pccard_set_LED(card, i, CCR_LED_FAST); break; case ACTIVITY_IDLE: pccard_set_LED(card, i, CCR_LED_SLOW); break; case ACTIVITY_INITIALIZED: pccard_set_LED(card, i, CCR_LED_ON); break; default: pccard_set_LED(card, i, CCR_LED_OFF); break; } } else pccard_set_LED(card, i, CCR_LED_OFF); } // restart timer if (card->run_activity_timer_cyclic) { card->activity_timer.expires = jiffies + HZ; add_timer(&card->activity_timer); }}static void pccard_start_activity_scanner(PCAN_PCCARD *card){ DPRINTK(KERN_DEBUG "%s: pccard_start_activity_scanner(%p)\n", DEVICE_NAME, card); init_timer(&card->activity_timer); card->activity_timer.function = pccard_activity_scanner;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -