📄 rtcan_peak_pci.c
字号:
/* * Copyright (C) 2006 Wolfgang Grandegger <wg@grandegger.com> * * Derived from the PCAN project file driver/src/pcan_pci.c: * * Copyright (C) 2001-2006 PEAK System-Technik GmbH * * * 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. */#include <linux/module.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/pci.h>#include <asm/io.h>#include <rtdm/rtdm_driver.h>/* CAN device profile */#include <rtdm/rtcan.h>#include <rtcan_dev.h>#include <rtcan_raw.h>#include <rtcan_sja1000.h>#include <rtcan_sja1000_regs.h>#define RTCAN_DEV_NAME "rtcan%d"#define RTCAN_DRV_NAME "PEAK-PCI-CAN"static char *peak_pci_board_name = "PEAK-PCI";MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");MODULE_DESCRIPTION("RTCAN board driver for PEAK-PCI cards");MODULE_SUPPORTED_DEVICE("PEAK-PCI card CAN controller");MODULE_LICENSE("GPL");struct rtcan_peak_pci{ struct pci_dev *pci_dev; struct rtcan_device *slave_dev; int channel; volatile void __iomem *base_addr; volatile void __iomem *conf_addr;};#define PEAK_PCI_CAN_SYS_CLOCK (16000000 / 2)#define PELICAN_SINGLE (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07 | SJA_CDR_CLK_OFF)#define PELICAN_MASTER (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07 )#define PELICAN_DEFAULT (SJA_CDR_CAN_MODE )#define CHANNEL_SINGLE 0 /* this is a single channel device */#define CHANNEL_MASTER 1 /* multi channel device, this device is master */#define CHANNEL_SLAVE 2 /* multi channel device, this is slave */// important PITA registers#define PITA_ICR 0x00 // interrupt control register#define PITA_GPIOICR 0x18 // general purpose IO interface control register#define PITA_MISC 0x1C // miscellanoes register#define PEAK_PCI_VENDOR_ID 0x001C // the PCI device and vendor IDs#define PEAK_PCI_DEVICE_ID 0x0001#define PCI_CONFIG_PORT_SIZE 0x1000 // size of the config io-memory#define PCI_PORT_SIZE 0x0400 // size of a channel io-memorystatic struct pci_device_id peak_pci_tbl[] = { {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},};MODULE_DEVICE_TABLE (pci, peak_pci_tbl);static u8 rtcan_peak_pci_read_reg(struct rtcan_device *dev, int port){ struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv; return readb(board->base_addr + ((unsigned long)port << 2));}static void rtcan_peak_pci_write_reg(struct rtcan_device *dev, int port, u8 data){ struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv; writeb(data, board->base_addr + ((unsigned long)port << 2));}static void rtcan_peak_pci_irq_ack(struct rtcan_device *dev){ struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv; u16 pita_icr_low; /* Select and clear in Pita stored interrupt */ pita_icr_low = readw(board->conf_addr + PITA_ICR); if (board->channel == CHANNEL_SLAVE) { if (pita_icr_low & 0x0001) writew(0x0001, board->conf_addr + PITA_ICR); } else { if (pita_icr_low & 0x0002) writew(0x0002, board->conf_addr + PITA_ICR); }}static void rtcan_peak_pci_del_chan(struct rtcan_device *dev, int init_step){ struct rtcan_peak_pci *board; u16 pita_icr_high; if (!dev) return; board = (struct rtcan_peak_pci *)dev->board_priv; switch (init_step) { case 0: /* Full cleanup */ printk("Removing %s %s device %s\n", peak_pci_board_name, dev->ctrl_name, dev->name); rtcan_sja1000_unregister(dev); case 5: pita_icr_high = readw(board->conf_addr + PITA_ICR + 2); if (board->channel == CHANNEL_SLAVE) { pita_icr_high &= ~0x0001; } else { pita_icr_high &= ~0x0002; } writew(pita_icr_high, board->conf_addr + PITA_ICR + 2); case 4: iounmap((void *)board->base_addr); case 3: if (board->channel != CHANNEL_SLAVE) iounmap((void *)board->conf_addr); case 2: rtcan_dev_free(dev); case 1: break; }}static int rtcan_peak_pci_add_chan(struct pci_dev *pdev, int channel, struct rtcan_device **master_dev){ struct rtcan_device *dev; struct rtcan_sja1000 *chip; struct rtcan_peak_pci *board; u16 pita_icr_high; unsigned long addr; int ret, init_step = 1; dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000), sizeof(struct rtcan_peak_pci)); if (dev == NULL) return -ENOMEM; init_step = 2; chip = (struct rtcan_sja1000 *)dev->priv; board = (struct rtcan_peak_pci *)dev->board_priv; board->pci_dev = pdev; board->channel = channel; if (channel != CHANNEL_SLAVE) { addr = pci_resource_start(pdev, 0); board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE); if (board->conf_addr == 0) { ret = -ENODEV; goto failure; } init_step = 3; /* Set GPIO control register */ writew(0x0005, board->conf_addr + PITA_GPIOICR + 2); if (channel == CHANNEL_MASTER) writeb(0x00, board->conf_addr + PITA_GPIOICR); /* enable both */ else writeb(0x04, board->conf_addr + PITA_GPIOICR); /* enable single */ writeb(0x05, board->conf_addr + PITA_MISC + 3); /* toggle reset */ mdelay(5); writeb(0x04, board->conf_addr + PITA_MISC + 3); /* leave parport mux mode */ } else { struct rtcan_peak_pci *master_board = (struct rtcan_peak_pci *)(*master_dev)->board_priv; master_board->slave_dev = dev; board->conf_addr = master_board->conf_addr; } addr = pci_resource_start(pdev, 1); if (channel == CHANNEL_SLAVE) addr += 0x400; board->base_addr = ioremap(addr, PCI_PORT_SIZE); if (board->base_addr == 0) { ret = -ENODEV; goto failure; } init_step = 4; dev->board_name = peak_pci_board_name; chip->read_reg = rtcan_peak_pci_read_reg; chip->write_reg = rtcan_peak_pci_write_reg; chip->irq_ack = rtcan_peak_pci_irq_ack; /* Clock frequency in Hz */ dev->can_sys_clock = PEAK_PCI_CAN_SYS_CLOCK; /* Output control register */ chip->ocr = SJA_OCR_MODE_NORMAL | SJA_OCR_TX0_PUSHPULL; /* Clock divider register */ if (channel == CHANNEL_MASTER) chip->cdr = PELICAN_MASTER; else chip->cdr = PELICAN_SINGLE; strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ); /* Register and setup interrupt handling */#ifdef CONFIG_XENO_OPT_SHIRQ_LEVEL chip->irq_flags = RTDM_IRQTYPE_SHARED;#else chip->irq_flags = 0;#endif chip->irq_num = pdev->irq; pita_icr_high = readw(board->conf_addr + PITA_ICR + 2); if (channel == CHANNEL_SLAVE) { pita_icr_high |= 0x0001; } else { pita_icr_high |= 0x0002; } writew(pita_icr_high, board->conf_addr + PITA_ICR + 2); init_step = 5; printk("%s: base_addr=%p conf_addr=%p irq=%d\n", RTCAN_DRV_NAME, board->base_addr, board->conf_addr, chip->irq_num); /* Register SJA1000 device */ ret = rtcan_sja1000_register(dev); if (ret) { printk(KERN_ERR "ERROR while trying to register SJA1000 device %d!\n", ret); goto failure; } if (channel != CHANNEL_SLAVE) *master_dev = dev; return 0; failure: rtcan_peak_pci_del_chan(dev, init_step); return ret;}static int __devinit peak_pci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent){ int ret; u16 sub_sys_id; struct rtcan_device *master_dev = NULL; printk("%s: initializing device %04x:%04x\n", RTCAN_DRV_NAME, pdev->vendor, pdev->device); if ((ret = pci_enable_device (pdev))) goto failure; if ((ret = pci_request_regions(pdev, RTCAN_DRV_NAME))) goto failure; if ((ret = pci_read_config_word(pdev, 0x2e, &sub_sys_id))) goto failure_cleanup; /* Enable memory space */ if ((ret = pci_write_config_word(pdev, 0x04, 2))) goto failure_cleanup; if ((ret = pci_write_config_word(pdev, 0x44, 0))) goto failure_cleanup; if (sub_sys_id > 3) { if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_MASTER, &master_dev))) goto failure_cleanup;#ifdef CONFIG_XENO_OPT_SHIRQ_LEVEL if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_SLAVE, &master_dev))) goto failure_cleanup;#else printk("Shared interrupts not enabled, using single channel!\n");#endif } else { if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_SINGLE, &master_dev))) goto failure_cleanup; } pci_set_drvdata(pdev, master_dev); return 0; failure_cleanup: if (master_dev) rtcan_peak_pci_del_chan(master_dev, 0); pci_release_regions(pdev); failure: return ret; }static void __devexit peak_pci_remove_one (struct pci_dev *pdev){ struct rtcan_device *dev = pci_get_drvdata(pdev); struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv; if (board->slave_dev) rtcan_peak_pci_del_chan(board->slave_dev, 0); rtcan_peak_pci_del_chan(dev, 0); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL);}static struct pci_driver rtcan_peak_pci_driver = { .name = RTCAN_DRV_NAME, .id_table = peak_pci_tbl, .probe = peak_pci_init_one, .remove = __devexit_p(peak_pci_remove_one),};static int __init rtcan_peak_pci_init(void){ return pci_module_init(&rtcan_peak_pci_driver);}static void __exit rtcan_peak_pci_exit(void){ pci_unregister_driver(&rtcan_peak_pci_driver);}module_init(rtcan_peak_pci_init);module_exit(rtcan_peak_pci_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -