From mboxrd@z Thu Jan 1 00:00:00 1970 From: Matthias Fuchs Subject: [PATCH] can: Add driver for SJA1000 based PCI CAN interface cards by esd Date: Wed, 31 Mar 2010 14:28:05 +0200 Message-ID: <201003311428.05749.matthias.fuchs@esd.eu> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: Socketcan-core@lists.berlios.de To: netdev@vger.kernel.org Return-path: Received: from moutng.kundenserver.de ([212.227.17.9]:62965 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932831Ab0CaMa1 (ORCPT ); Wed, 31 Mar 2010 08:30:27 -0400 Content-Disposition: inline Sender: netdev-owner@vger.kernel.org List-ID: This patch adds support for SJA1000 based PCI CAN interface cards from electronic system design gmbh. These boards are supported: CAN-PCI/200 (PCI) CAN-PCI/266 (PCI) CAN-PMC266 (PMC module) CAN-PCIe/2000 (PCI Express) CAN-CPCI/200 (Compact PCI, 3U) CAN-PCI104 (PCI104) This driver is part of the SocketCAN SVN repository since April 2009. Signed-off-by: Matthias Fuchs --- drivers/net/can/sja1000/Kconfig | 9 + drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/esd_pci.c | 315 +++++++++++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/esd_pci.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index 9e277d6..c01eb4e 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -37,6 +37,15 @@ config CAN_EMS_PCI CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). +config CAN_ESD_PCI + tristate "ESD PCI Cards" + depends on PCI + ---help--- + This driver supports the esd PCI CAN cards CAN-PCI/200, + CAN-PCI/266, CAN-PMC/266 (PMC), CAN-CPCI/200 (CompactPCI), + CAN-PCIe2000 (PCI Express) and CAN-PCI104/200 (PCI104) + from the esd electronic system design gmbh (http://www.esd.eu). + config CAN_KVASER_PCI tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" depends on PCI diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index ce92455..a8dcbe0 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o +obj-$(CONFIG_CAN_ESD_PCI) += esd_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o diff --git a/drivers/net/can/sja1000/esd_pci.c b/drivers/net/can/sja1000/esd_pci.c new file mode 100644 index 0000000..98389f2 --- /dev/null +++ b/drivers/net/can/sja1000/esd_pci.c @@ -0,0 +1,315 @@ +/* + * 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 +#include +#include +#include + +#include "sja1000.h" + +#define DRV_NAME "esd_pci" + +MODULE_AUTHOR("Matthias Fuchs reg_base + port); +} + +static void esd_pci_write_reg(const struct sja1000_priv *priv, int port, u8 val) +{ + writeb(val, priv->reg_base + port); +} + +static void esd_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev) +{ + dev_info(&pdev->dev, "Removing device %s\n", ndev->name); + + unregister_sja1000dev(ndev); + + free_sja1000dev(ndev); +} + +static struct net_device * __devinit esd_pci_add_chan(struct pci_dev *pdev, + void __iomem *base_addr) +{ + struct net_device *ndev; + struct sja1000_priv *priv; + int err; + + ndev = alloc_sja1000dev(0); + if (ndev == NULL) + return ERR_PTR(-ENOMEM); + + priv = netdev_priv(ndev); + + priv->reg_base = base_addr; + + priv->read_reg = esd_pci_read_reg; + priv->write_reg = esd_pci_write_reg; + + priv->can.clock.freq = ESD_PCI_CAN_CLOCK; + + priv->ocr = ESD_PCI_OCR; + priv->cdr = ESD_PCI_CDR; + + /* Set and enable PCI interrupts */ + priv->irq_flags = IRQF_SHARED; + ndev->irq = pdev->irq; + + dev_dbg(&pdev->dev, "reg_base=0x%p irq=%d\n", + priv->reg_base, ndev->irq); + + SET_NETDEV_DEV(ndev, &pdev->dev); + + err = register_sja1000dev(ndev); + if (err) { + dev_err(&pdev->dev, "Failed to register (err=%d)\n", err); + goto failure; + } + + return ndev; + +failure: + free_sja1000dev(ndev); + return ERR_PTR(err); +} + +static int __devinit esd_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct esd_pci *board; + int err; + void __iomem *base_addr; + void __iomem *conf_addr; + + dev_info(&pdev->dev, + "Initializing device %04x:%04x %04x:%04x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + board = kzalloc(sizeof(*board), GFP_KERNEL); + if (!board) + return -ENOMEM; + + err = pci_enable_device(pdev); + if (err) + goto failure; + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto failure; + + conf_addr = pci_iomap(pdev, 0, ESD_PCI_BASE_SIZE); + if (conf_addr == NULL) { + err = -ENODEV; + goto failure_release_pci; + } + + board->conf_addr = conf_addr; + + base_addr = pci_iomap(pdev, 2, ESD_PCI_BASE_SIZE); + if (base_addr == NULL) { + err = -ENODEV; + goto failure_iounmap_conf; + } + + board->base_addr = base_addr; + + board->dev[0] = esd_pci_add_chan(pdev, base_addr); + if (IS_ERR(board->dev[0])) + goto failure_iounmap_base; + + /* Check if second channel is available */ + writeb(MOD_RM, base_addr + CHANNEL_OFFSET + REG_MOD); + writeb(CDR_CBP, base_addr + CHANNEL_OFFSET + REG_CDR); + writeb(MOD_RM, base_addr + CHANNEL_OFFSET + REG_MOD); + if (readb(base_addr + CHANNEL_OFFSET + REG_MOD) == 0x21) { + writeb(MOD_SM | MOD_AFM | MOD_STM | MOD_LOM | MOD_RM, + base_addr + CHANNEL_OFFSET + REG_MOD); + if (readb(base_addr + CHANNEL_OFFSET + REG_MOD) == 0x3f) { + writeb(MOD_RM, base_addr + CHANNEL_OFFSET + REG_MOD); + board->dev[1] = + esd_pci_add_chan(pdev, + base_addr + CHANNEL_OFFSET); + if (IS_ERR(board->dev[1])) + goto failure_unreg_dev0; + } else + writeb(MOD_RM, base_addr + CHANNEL_OFFSET + REG_MOD); + } else + writeb(MOD_RM, base_addr + CHANNEL_OFFSET + REG_MOD); + + if ((pdev->device == PCI_DEVICE_ID_PLX_9050) || + (pdev->device == PCI_DEVICE_ID_PLX_9030)) { + /* Enable interrupts in PLX9050 */ + writel(INTCSR_LINTI1 | INTCSR_PCI, + board->conf_addr + INTCSR_OFFSET); + } else { + /* Enable interrupts in PLX9056*/ + writel(INTCSR9056_LINTI | INTCSR9056_PCI, + board->conf_addr + INTCSR9056_OFFSET); + } + + pci_set_drvdata(pdev, board); + + return 0; + +failure_unreg_dev0: + esd_pci_del_chan(pdev, board->dev[0]); + +failure_iounmap_base: + pci_iounmap(pdev, board->base_addr); + +failure_iounmap_conf: + pci_iounmap(pdev, board->conf_addr); + +failure_release_pci: + pci_release_regions(pdev); + +failure: + kfree(board); + + return err; +} + +static void esd_pci_remove_one(struct pci_dev *pdev) +{ + struct esd_pci *board = pci_get_drvdata(pdev); + int i; + + 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); + } + + for (i = 0; i < ESD_PCI_MAX_CAN; i++) { + if (!board->dev[i]) + break; + esd_pci_del_chan(pdev, board->dev[i]); + } + + 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); + + kfree(board); +} + +static struct pci_driver esd_pci_driver = { + .name = DRV_NAME, + .id_table = esd_pci_tbl, + .probe = esd_pci_init_one, + .remove = esd_pci_remove_one, +}; + +static int __init esd_pci_init(void) +{ + return pci_register_driver(&esd_pci_driver); +} + +static void __exit esd_pci_exit(void) +{ + pci_unregister_driver(&esd_pci_driver); +} + +module_init(esd_pci_init); +module_exit(esd_pci_exit); -- 1.6.1