This patch adds support for SJA1000 based PCI CAN interface cards from 
electronic system design gmbh.

The following list of boards are supported:

CAN-PCI/200 (tested)
CAN-PCI/266
CAN-PMC266
CAN-PCIe/2000
CAN-CPCI/200
CAN-PCI104

The patch is based on the Socket-CAN driver for those boards by Matthias 
Fuchs.

Signed-off-by: Sebastian Smolorz <smol...@rts.uni-hannover.de>
-- 

 ksrc/drivers/can/sja1000/Kconfig         |   11 +
 ksrc/drivers/can/sja1000/Makefile        |    7 +
 ksrc/drivers/can/sja1000/rtcan_esd_pci.c |  354 ++++++++++++++++++++++++++++++
 3 files changed, 372 insertions(+), 0 deletions(-)
diff --git a/ksrc/drivers/can/sja1000/Kconfig b/ksrc/drivers/can/sja1000/Kconfig
index 4d0ed48..53abdd9 100644
--- a/ksrc/drivers/can/sja1000/Kconfig
+++ b/ksrc/drivers/can/sja1000/Kconfig
@@ -52,6 +52,17 @@ config XENO_DRIVERS_CAN_SJA1000_EMS_PCI
 	working, Xenomai's shared interrupt support must be enabled.
 
 
+config XENO_DRIVERS_CAN_SJA1000_ESD_PCI
+	depends on XENO_DRIVERS_CAN_SJA1000
+	tristate "ESD PCI Cards"
+	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 XENO_DRIVERS_CAN_SJA1000_PEAK_DNG
 	depends on XENO_DRIVERS_CAN_SJA1000
 	tristate "PEAK Parallel Port Dongle"
diff --git a/ksrc/drivers/can/sja1000/Makefile b/ksrc/drivers/can/sja1000/Makefile
index 1db102e..09a03ab 100644
--- a/ksrc/drivers/can/sja1000/Makefile
+++ b/ksrc/drivers/can/sja1000/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_PCI) += xeno_can_peak_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_DNG) += xeno_can_peak_dng.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_IXXAT_PCI) += xeno_can_ixxat_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_EMS_PCI) += xeno_can_ems_pci.o
+obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_ESD_PCI) += xeno_can_esd_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_ISA) += xeno_can_isa.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_MEM) += xeno_can_mem.o
 
@@ -17,6 +18,7 @@ xeno_can_peak_pci-y := rtcan_peak_pci.o
 xeno_can_peak_dng-y := rtcan_peak_dng.o
 xeno_can_ixxat_pci-y := rtcan_ixxat_pci.o
 xeno_can_ems_pci-y := rtcan_ems_pci.o
+xeno_can_esd_pci-y := rtcan_esd_pci.o
 xeno_can_isa-y := rtcan_isa.o
 xeno_can_mem-y := rtcan_mem.o
 
@@ -31,6 +33,7 @@ obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_PCI) += xeno_can_peak_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_DNG) += xeno_can_peak_dng.o 
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_IXXAT_PCI) += xeno_can_ixxat_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_EMS_PCI) += xeno_can_ems_pci.o
+obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_ESD_PCI) += xeno_can_esd_pci.o
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_ISA) += xeno_can_isa.o 
 obj-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_MEM) += xeno_can_mem.o
 
@@ -41,6 +44,7 @@ xeno_can_peak_pci-objs := rtcan_peak_pci.o
 xeno_can_peak_dng-objs := rtcan_peak_dng.o
 xeno_can_ixxat_pci-objs := rtcan_ixxat_pci.o
 xeno_can_ems_pci-objs := rtcan_ems_pci.o
+xeno_can_esd_pci-objs := rtcan_esd_pci.o
 xeno_can_isa-objs := rtcan_isa.o
 xeno_can_mem-objs := rtcan_mem.o
 
@@ -65,6 +69,9 @@ xeno_can_ixxat_pci.o: $(xeno_can_ixxat_pci-objs)
 xeno_can_ems_pci.o: $(xeno_can_ems_pci-objs)
 	$(LD) -r -o $@ $(xeno_can_ems_pci-objs)
 
+xeno_can_esd_pci.o: $(xeno_can_esd_pci-objs)
+	$(LD) -r -o $@ $(xeno_can_esd_pci-objs)
+
 xeno_can_isa.o: $(xeno_can_isa-objs)
 	$(LD) -r -o $@ $(xeno_can_isa-objs)
 
diff --git a/ksrc/drivers/can/sja1000/rtcan_esd_pci.c b/ksrc/drivers/can/sja1000/rtcan_esd_pci.c
new file mode 100644
index 0000000..79cf8b6
--- /dev/null
+++ b/ksrc/drivers/can/sja1000/rtcan_esd_pci.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2009 Sebastian Smolorz <se...@gmx.net>
+ *
+ * This driver is based on the Socket-CAN driver esd_pci.c,
+ * Copyright (C) 2007 Wolfgang Grandegger <w...@grandegger.com>
+ * Copyright (C) 2008 Sascha Hauer <s.ha...@pengutronix.de>, Pengutronix
+ * Copyright (C) 2009 Matthias Fuchs <matthias.fu...@esd.eu>, 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 <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_internal.h>
+#include <rtcan_sja1000.h>
+#include <rtcan_sja1000_regs.h>
+
+#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 <se...@gmx.net");
+MODULE_DESCRIPTION("RTCAN board driver for esd PCI/PMC/CPCI/PCIe/PCI104 " \
+                   "CAN cards");
+MODULE_SUPPORTED_DEVICE("esd CAN-PCI/200, CAN-PCI/266, CAN-PMC266, " \
+			"CAN-PCIe/2000, CAN-CPCI/200, CAN-PCI104");
+MODULE_LICENSE("GPL v2");
+
+struct rtcan_esd_pci {
+	struct pci_dev *pci_dev;
+	struct rtcan_device *slave_dev;
+	void __iomem *conf_addr;
+	void __iomem *base_addr;
+};
+
+#define ESD_PCI_CAN_CLOCK	(16000000 / 2)
+
+#define ESD_PCI_OCR		(SJA_OCR_TX0_PUSHPULL | SJA_OCR_TX1_PUSHPULL | \
+				 SJA_OCR_TX1_INVERT | SJA_OCR_MODE_CLOCK)
+#define ESD_PCI_CDR		(SJA_CDR_CLK_OFF | SJA_CDR_CBP | \
+				 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 */
+
+#define CHANNEL_OFFSET		0x100
+
+#define INTCSR_OFFSET		0x4c /* Offset in PLX9050 conf registers */
+#define INTCSR_LINTI1		(1 << 0)
+#define INTCSR_PCI		(1 << 6)
+
+#define INTCSR9056_OFFSET	0x68 /* Offset in PLX9056 conf registers */
+#define INTCSR9056_LINTI	(1 << 11)
+#define INTCSR9056_PCI		(1 << 8)
+
+#ifndef PCI_DEVICE_ID_PLX_9056
+# define PCI_DEVICE_ID_PLX_9056 0x9056
+#endif
+
+/* PCI subsystem IDs of esd's SJA1000 based CAN cards */
+
+/* CAN-PCI/200: PCI, 33MHz only, bridge: PLX9050 */
+#define ESD_PCI_SUB_SYS_ID_PCI200	0x0004
+
+/* CAN-PCI/266: PCI, 33/66MHz, bridge: PLX9056 */
+#define ESD_PCI_SUB_SYS_ID_PCI266	0x0009
+
+/* CAN-PMC/266: PMC module, 33/66MHz, bridge: PLX9056 */
+#define ESD_PCI_SUB_SYS_ID_PMC266	0x000e
+
+/* CAN-CPCI/200: Compact PCI, 33MHz only, bridge: PLX9030 */
+#define ESD_PCI_SUB_SYS_ID_CPCI200	0x010b
+
+/* CAN-PCIE/2000: PCI Express 1x, bridge: PEX8311 = PEX8111 + PLX9056 */
+#define ESD_PCI_SUB_SYS_ID_PCIE2000	0x0200
+
+/* CAN-PCI/104: PCI104 module, 33MHz only, bridge: PLX9030 */
+#define ESD_PCI_SUB_SYS_ID_PCI104200	0x0501
+
+static struct pci_device_id esd_pci_tbl[] = {
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200},
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266},
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266},
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200},
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000},
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
+	 PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200},
+	{0,}
+};
+
+#define ESD_PCI_BASE_SIZE  0x200
+
+MODULE_DEVICE_TABLE(pci, esd_pci_tbl);
+
+
+static u8 rtcan_esd_pci_read_reg(struct rtcan_device *dev, int port)
+{
+	struct rtcan_esd_pci *board = (struct rtcan_esd_pci *)dev->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 __devinit 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 ((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 __devexit 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 = __devexit_p(esd_pci_remove_one),
+};
+
+static int __init rtcan_esd_pci_init(void)
+{
+	return pci_register_driver(&rtcan_esd_pci_driver);
+}
+
+static void __exit rtcan_esd_pci_exit(void)
+{
+	pci_unregister_driver(&rtcan_esd_pci_driver);
+}
+
+module_init(rtcan_esd_pci_init);
+module_exit(rtcan_esd_pci_exit);
_______________________________________________
Xenomai-core mailing list
Xenomai-core@gna.org
https://mail.gna.org/listinfo/xenomai-core

Reply via email to