/* * Copyright (C) 2009 Sebastian Smolorz * * This driver is based on the Socket-CAN driver esd_pci.c, * Copyright (C) 2007 Wolfgang Grandegger * Copyright (C) 2008 Sascha Hauer , Pengutronix * Copyright (C) 2009 Matthias Fuchs , esd gmbh * * This program is free software; you can redistribute it and/or modify * it under the terms of the version 2 of the GNU General Public License * as published by the Free Software Foundation * * 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 #include #include #include #include #include /* CAN device profile */ #include #include #include #include #include #include #define RTCAN_DEV_NAME "rtcan%d" #define RTCAN_DRV_NAME "ESD-PCI-CAN" static char *esd_pci_board_name = "ESD-PCI"; MODULE_AUTHOR("Sebastian Smolorz board_priv; return readb(board->base_addr + port); } static void rtcan_esd_pci_write_reg(struct rtcan_device *dev, int port, u8 val) { struct rtcan_esd_pci *board = (struct rtcan_esd_pci *)dev->board_priv; writeb(val, board->base_addr + port); } static void rtcan_esd_pci_del_chan(struct rtcan_device *dev) { struct rtcan_esd_pci *board; if (!dev) return; board = (struct rtcan_esd_pci *)dev->board_priv; printk("Removing %s %s device %s\n", esd_pci_board_name, dev->ctrl_name, dev->name); rtcan_sja1000_unregister(dev); rtcan_dev_free(dev); } static int rtcan_esd_pci_add_chan(struct pci_dev *pdev, int channel, struct rtcan_device **master_dev, void __iomem *conf_addr, void __iomem *base_addr) { struct rtcan_device *dev; struct rtcan_sja1000 *chip; struct rtcan_esd_pci *board; int ret; dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000), sizeof(struct rtcan_esd_pci)); if (dev == NULL) return -ENOMEM; chip = (struct rtcan_sja1000 *)dev->priv; board = (struct rtcan_esd_pci *)dev->board_priv; board->pci_dev = pdev; board->conf_addr = conf_addr; board->base_addr = base_addr; if (channel == CHANNEL_SLAVE) { struct rtcan_esd_pci *master_board = (struct rtcan_esd_pci *)(*master_dev)->board_priv; master_board->slave_dev = dev; } dev->board_name = esd_pci_board_name; chip->read_reg = rtcan_esd_pci_read_reg; chip->write_reg = rtcan_esd_pci_write_reg; dev->can_sys_clock = ESD_PCI_CAN_CLOCK; chip->ocr = ESD_PCI_OCR; chip->cdr = ESD_PCI_CDR; strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ); chip->irq_flags = RTDM_IRQTYPE_SHARED; chip->irq_num = pdev->irq; RTCAN_DBG("%s: base_addr=0x%p conf_addr=0x%p irq=%d ocr=%#x cdr=%#x\n", RTCAN_DRV_NAME, board->base_addr, board->conf_addr, chip->irq_num, chip->ocr, chip->cdr); /* Register SJA1000 device */ ret = rtcan_sja1000_register(dev); if (ret) { printk(KERN_ERR "ERROR %d while trying to register SJA1000 " "device!\n", ret); goto failure; } if (channel != CHANNEL_SLAVE) *master_dev = dev; return 0; failure: rtcan_dev_free(dev); return ret; } static int esd_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret, channel; void __iomem *base_addr; void __iomem *conf_addr; struct rtcan_device *master_dev = NULL; if (!rtdm_available()) return -ENODEV; if ((ret = pci_enable_device (pdev))) goto failure; if ((ret = pci_request_regions(pdev, RTCAN_DRV_NAME))) goto failure; RTCAN_DBG("%s: Initializing device %04x:%04x %04x:%04x\n", RTCAN_DRV_NAME, pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device); conf_addr = pci_iomap(pdev, 0, ESD_PCI_BASE_SIZE); if (conf_addr == NULL) { ret = -ENODEV; goto failure_release_pci; } base_addr = pci_iomap(pdev, 2, ESD_PCI_BASE_SIZE); if (base_addr == NULL) { ret = -ENODEV; goto failure_iounmap_conf; } /* Check if second channel is available */ writeb(SJA_MOD_RM, base_addr + CHANNEL_OFFSET + SJA_MOD); writeb(SJA_CDR_CBP, base_addr + CHANNEL_OFFSET + SJA_CDR); writeb(SJA_MOD_RM, base_addr + CHANNEL_OFFSET + SJA_MOD); if (readb(base_addr + CHANNEL_OFFSET + SJA_MOD) == 0x21) { writeb(SJA_MOD_SM | SJA_MOD_AFM | SJA_MOD_STM | SJA_MOD_LOM | SJA_MOD_RM, base_addr + CHANNEL_OFFSET + SJA_MOD); if (readb(base_addr + CHANNEL_OFFSET + SJA_MOD) == 0x3f) channel = CHANNEL_MASTER; else { writeb(SJA_MOD_RM, base_addr + CHANNEL_OFFSET + SJA_MOD); channel = CHANNEL_SINGLE; } } else { writeb(SJA_MOD_RM, base_addr + CHANNEL_OFFSET + SJA_MOD); channel = CHANNEL_SINGLE; } if ((ret = rtcan_esd_pci_add_chan(pdev, channel, &master_dev, conf_addr, base_addr))) goto failure_iounmap_base; if (channel != CHANNEL_SINGLE) { channel = CHANNEL_SLAVE; if ((ret = rtcan_esd_pci_add_chan(pdev, channel, &master_dev, conf_addr, base_addr + CHANNEL_OFFSET))) goto failure_iounmap_base; } if ((pdev->device == PCI_DEVICE_ID_PLX_9050) || (pdev->device == PCI_DEVICE_ID_PLX_9030)) { /* Enable interrupts in PLX9050 */ writel(INTCSR_LINTI1 | INTCSR_PCI, conf_addr + INTCSR_OFFSET); } else { /* Enable interrupts in PLX9056*/ writel(INTCSR9056_LINTI | INTCSR9056_PCI, conf_addr + INTCSR9056_OFFSET); } pci_set_drvdata(pdev, master_dev); return 0; failure_iounmap_base: if (master_dev) rtcan_esd_pci_del_chan(master_dev); pci_iounmap(pdev, base_addr); failure_iounmap_conf: pci_iounmap(pdev, conf_addr); failure_release_pci: pci_release_regions(pdev); failure: return ret; } static void esd_pci_remove_one(struct pci_dev *pdev) { struct rtcan_device *dev = pci_get_drvdata(pdev); struct rtcan_esd_pci *board = (struct rtcan_esd_pci *)dev->board_priv; if ((pdev->device == PCI_DEVICE_ID_PLX_9050) || (pdev->device == PCI_DEVICE_ID_PLX_9030)) { /* Disable interrupts in PLX9050*/ writel(0, board->conf_addr + INTCSR_OFFSET); } else { /* Disable interrupts in PLX9056*/ writel(0, board->conf_addr + INTCSR9056_OFFSET); } if (board->slave_dev) rtcan_esd_pci_del_chan(board->slave_dev); rtcan_esd_pci_del_chan(dev); pci_iounmap(pdev, board->base_addr); pci_iounmap(pdev, board->conf_addr); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } static struct pci_driver rtcan_esd_pci_driver = { .name = RTCAN_DRV_NAME, .id_table = esd_pci_tbl, .probe = esd_pci_init_one, .remove = esd_pci_remove_one, }; module_pci_driver(rtcan_esd_pci_driver);