Hi!
This driver (plx_pci) adds support for some sja1000 CAN controllers
which is based on PLX PCI bridges.
It works with:
- Marathon CAN-bus-PCI card (http://www.marathon.ru/)
- Adlink PCI-7841/cPCI-7841 card (http://www.adlinktech.com/)
- Adlink PCI-7841/cPCI-7841 SE card (it's a slight modification of
previous card)
- TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/)
I'm not sure that plx_pci is a right name for this driver. But I think
this driver have certain potentialities to support any other similar
cards (based on PLX bridges).
Attached patch against revision 1095 of socketcan SVN.
I hope it will be useful for anybody else.
---
Pavel Cheblakov,
The Budker Institute of Nuclear Physics
Novosibirsk, Russia
Index: kernel/2.6/drivers/net/can/sja1000/plx_pci.c
===================================================================
--- kernel/2.6/drivers/net/can/sja1000/plx_pci.c (.../svn+ssh://chebla...@v5cvs/socketcan/vendor/socketcan/1095) (revision 0)
+++ kernel/2.6/drivers/net/can/sja1000/plx_pci.c (revision 199)
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2008-2009 Pavel Cheblakov <[email protected]>
+ *
+ * Derived from the ems_pci.c driver:
+ * Copyright (C) 2007 Wolfgang Grandegger <[email protected]>
+ * Copyright (C) 2008 Markus Plessing <[email protected]>
+ * Copyright (C) 2008 Sebastian Haas <[email protected]>
+ *
+ *
+ * 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.
+ */
+
+/*
+ Driver plx_pci, version 1.1.0, 2009-12-26
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "plx_pci"
+
+MODULE_AUTHOR("Pavel Cheblakov <[email protected]>");
+MODULE_DESCRIPTION("Socket-CAN driver for PLX9xxx PCI-bridge cards with "
+ "sja1000 chips");
+MODULE_SUPPORTED_DEVICE("Marathon CAN-bus-PCI, "
+ "Adlink PCI-7841/cPCI-7841, "
+ "Adlink PCI-7841/cPCI-7841 SE, "
+ "TEWS TECHNOLOGIES TPMC810");
+MODULE_LICENSE("GPL v2");
+
+#define PLX_PCI_MAX_CHAN 2
+
+struct plx_pci_card {
+ int channels; /* detected channels count */
+
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev[PLX_PCI_MAX_CHAN];
+
+ void __iomem *conf_addr;
+ void __iomem *base_addr[PLX_PCI_MAX_CHAN];
+};
+
+#define PLX_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * PLX9xxx registers
+ */
+#define PLX_INTCSR 0x4c /* Interrup Control/Status */
+#define PLX_CNTRL 0x50 /* User I/O, Direct Slave Response,
+ Serial EEPROM, and Initialization
+ Control register */
+
+#define PLX_LINT1_EN 0x1 /* Local interrupt 1 enable */
+#define PLX_LINT2_EN (1 << 3) /* Local interrupt 2 enable */
+#define PLX_PCI_INT_EN (1 << 6) /* PCI Interrupt Enable */
+#define PLX_PCI_RESET (1 << 30) /* PCI Adapter Software Reset */
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define PLX_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define PLX_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+
+#define ADLINK_PCI_VENDOR_ID 0x144A
+#define ADLINK_PCI_DEVICE_ID 0x7841
+
+#define MARATHON_PCI_DEVICE_ID 0x2715
+
+#define TEWS_PCI_VENDOR_ID 0x1498
+#define TEWS_PCI_DEVICE_ID_TMPC810 0x032A
+
+enum plx_cards {
+ MARATHON_CAN_BUS_PCI = 0,
+ ADLINK_PCI_7841,
+ ADLINK_PCI_7841_SE,
+ TEWS_TPMC810
+};
+
+struct channel_map {
+ u8 bar;
+ u32 offset;
+ u32 size; /* 0x00 - auto, e.g. length of entire bar */
+};
+
+static struct plx_card_info {
+ const char *name;
+ u8 channel_count;
+ int can_clock;
+
+ /* Parameters for mapping local configuration space is located
+ * at index 0 in this array.
+ * Subsequent array elements correspond to mapping SJA1000 chips.
+ */
+ struct channel_map ch_map_tbl[PLX_PCI_MAX_CHAN + 1];
+} plx_card_info_tbl[] __devinitdata = {
+ {"Marathon CAN-bus-PCI",
+ 2, PLX_PCI_CAN_CLOCK, { {0, 0x00, 0x00},
+ {2, 0x00, 0x00},
+ {4, 0x00, 0x00} }
+ },
+ {"Adlink PCI-7841/cPCI-7841",
+ 2, PLX_PCI_CAN_CLOCK, { {1, 0x00, 0x00},
+ {2, 0x00, 0x80},
+ {2, 0x80, 0x80} }
+ },
+ {"Adlink PCI-7841/cPCI-7841 SE",
+ 2, PLX_PCI_CAN_CLOCK, { {0, 0x00, 0x00},
+ {2, 0x00, 0x80},
+ {2, 0x80, 0x80} }
+ },
+ {"TEWS TECHNOLOGIES TPMC810",
+ 2, PLX_PCI_CAN_CLOCK, { {0, 0x00, 0x00},
+ {2, 0x000, 0x80},
+ {2, 0x100, 0x80} }
+ },
+ { NULL }
+};
+
+static struct pci_device_id plx_pci_tbl[] = {
+
+ { /* Marathon CAN-bus-PCI card */
+ PCI_VENDOR_ID_PLX, MARATHON_PCI_DEVICE_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0,
+ MARATHON_CAN_BUS_PCI
+ },
+
+ { /* Adlink PCI-7841/cPCI-7841 */
+ ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_OTHER << 8, ~0,
+ ADLINK_PCI_7841
+ },
+ { /* Adlink PCI-7841/cPCI-7841 SE */
+ ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_OTHER << 8, ~0,
+ ADLINK_PCI_7841_SE
+ },
+ { /* TEWS TECHNOLOGIES TPMC810 card */
+ TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0,
+ TEWS_TPMC810
+ },
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
+
+static u8 plx_pci_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + port);
+}
+
+static void plx_pci_write_reg(const struct sja1000_priv *priv, int port, u8 val)
+{
+ writeb(val, priv->reg_base + port);
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int plx_pci_check_sja1000(const struct sja1000_priv *priv)
+{
+ u8 res;
+
+ /* Make sure SJA1000 is in reset mode */
+ priv->write_reg(priv, REG_MOD, 1);
+
+ priv->write_reg(priv, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = priv->read_reg(priv, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Software reset PLX9xxx
+ * Also LRESET# asserts and brings to reset device on the Local Bus
+ */
+static void plx_pci_reset(struct plx_pci_card *card)
+{
+ u32 cntrl;
+
+ cntrl = ioread32(card->conf_addr + PLX_CNTRL);
+ cntrl |= PLX_PCI_RESET;
+ iowrite32(cntrl, card->conf_addr + PLX_CNTRL);
+ udelay(100);
+ cntrl ^= PLX_PCI_RESET;
+ iowrite32(cntrl, card->conf_addr + PLX_CNTRL);
+};
+
+static void plx_pci_del_card(struct pci_dev *pdev)
+{
+ struct plx_pci_card *card = pci_get_drvdata(pdev);
+ struct net_device *dev;
+ int i = 0;
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+
+ if (card->base_addr[i] != NULL)
+ pci_iounmap(card->pci_dev, card->base_addr[i]);
+ }
+
+ plx_pci_reset(card);
+
+ /* Disable interrupts from PCI-card (PLX9xxx) and disable Local_1,
+ * Local_2 interrupts */
+ iowrite32(0x0, card->conf_addr + PLX_INTCSR);
+
+ if (card->conf_addr != NULL)
+ pci_iounmap(card->pci_dev, card->conf_addr);
+
+ kfree(card);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+/*
+ * Probe PLX9xxx based device for SJA1000 chips and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit plx_pci_add_card(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct plx_pci_card *card;
+ struct plx_card_info *ci;
+ int err, i;
+ u32 tmp;
+
+ ci = &plx_card_info_tbl[ent->driver_data];
+
+ if (pci_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "Failed to enable PCI device\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "Detected \"%s\" card at slot #%i\n",
+ ci->name, PCI_SLOT(pdev->devfn));
+
+ /* Allocate card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct plx_pci_card), GFP_KERNEL);
+ if (card == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ pci_disable_device(pdev);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, card);
+
+ card->pci_dev = pdev;
+ card->channels = 0;
+
+ /* Remap PLX9xxx configuration space */
+ card->conf_addr = pci_iomap(pdev, ci->ch_map_tbl[0].bar,
+ ci->ch_map_tbl[0].size) +
+ ci->ch_map_tbl[0].offset;
+ /* this calculation only for commonality */
+ if (card->conf_addr == NULL) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev,
+ "Failed to remap configuration space (BAR%d)\n",
+ ci->ch_map_tbl[0].bar);
+ goto failure_cleanup;
+ }
+ dev_dbg(&pdev->dev, "Configuration space remaped to 0x%x\n",
+ (u32)card->conf_addr);
+
+ plx_pci_reset(card);
+
+ /* Detect available channels */
+ for (i = 0; i < ci->channel_count; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+
+ dev->irq = pdev->irq;
+
+ /*
+ * Remap IO space of SJA1000 chips
+ * This is device-dependent mapping
+ */
+ card->base_addr[i] = pci_iomap(pdev, ci->ch_map_tbl[i+1].bar,
+ ci->ch_map_tbl[i+1].size) +
+ ci->ch_map_tbl[i+1].offset;
+ if (card->base_addr[i] == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+ dev_dbg(&pdev->dev, "IO space of chip #%d remaped to 0x%x\n",
+ i+1, (u32)card->base_addr[i]);
+ dev->base_addr = (u32)card->base_addr[i];
+
+ priv->reg_base = (void *)card->base_addr[i];
+ priv->read_reg = plx_pci_read_reg;
+ priv->write_reg = plx_pci_write_reg;
+
+ /* Check if channel is present */
+ if (plx_pci_check_sja1000(priv)) {
+ priv->can.clock.freq = ci->can_clock;
+ priv->ocr = PLX_PCI_OCR;
+ priv->cdr = PLX_PCI_CDR;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed "
+ "(err=%d)\n", err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ dev_info(&pdev->dev, "Channel #%d at 0x%x, irq %d "
+ "registered as %s\n",
+ i + 1, (u32)dev->base_addr,
+ dev->irq, dev->name);
+ } else {
+ dev_err(&pdev->dev, "Channel #%d not detected\n", i+1);
+ free_sja1000dev(dev);
+ }
+ }
+
+ /* Enable interrupts from PCI-card (PLX9xxx) and enable Local_1,
+ * Local_2 interrupts from SJA1000 chips */
+ tmp = ioread32(card->conf_addr + PLX_INTCSR);
+ tmp |= PLX_LINT1_EN | PLX_LINT2_EN | PLX_PCI_INT_EN;
+ iowrite32(tmp, card->conf_addr + PLX_INTCSR);
+
+ return 0;
+
+failure_cleanup:
+ dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
+
+ plx_pci_del_card(pdev);
+
+ return err;
+}
+
+static struct pci_driver plx_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = plx_pci_tbl,
+ .probe = plx_pci_add_card,
+ .remove = plx_pci_del_card,
+};
+
+static int __init plx_pci_init(void)
+{
+ return pci_register_driver(&plx_pci_driver);
+}
+
+static void __exit plx_pci_exit(void)
+{
+ pci_unregister_driver(&plx_pci_driver);
+}
+
+module_init(plx_pci_init);
+module_exit(plx_pci_exit);
+
Index: kernel/2.6/drivers/net/can/sja1000/Kconfig
===================================================================
--- kernel/2.6/drivers/net/can/sja1000/Kconfig (.../svn+ssh://chebla...@v5cvs/socketcan/vendor/socketcan/1095) (revision 199)
+++ kernel/2.6/drivers/net/can/sja1000/Kconfig (working copy)
@@ -36,6 +36,17 @@
This driver is for the one, two or four channel CPC-PCI,
CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
(http://www.ems-wuensche.de).
+
+config CAN_PLX_PCI
+ tristate "Socket-CAN driver for PLX9xxx PCI-bridge cards with sja1000 chips"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for CAN interface cards which based on PLX9xxx PCI bridge.
+ Driver supports now:
+ - Marathon CAN-bus-PCI card (http://www.marathon.ru/)
+ - Adlink PCI-7841/cPCI-7841 card (http://www.adlinktech.com/)
+ - Adlink PCI-7841/cPCI-7841 SE card
+ - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/)
config CAN_EMS_PCMCIA
tristate "EMS CPC-CARD Card"
Index: kernel/2.6/drivers/net/can/sja1000/Makefile
===================================================================
--- kernel/2.6/drivers/net/can/sja1000/Makefile (.../svn+ssh://chebla...@v5cvs/socketcan/vendor/socketcan/1095) (revision 199)
+++ kernel/2.6/drivers/net/can/sja1000/Makefile (working copy)
@@ -20,6 +20,7 @@
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_PLX_PCI) += plx_pci.o
obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
obj-$(CONFIG_CAN_EMS_104M) += ems_104m.o
obj-$(CONFIG_CAN_ESD_PCI) += esd_pci.o
Index: kernel/2.6/drivers/net/can/Makefile
===================================================================
--- kernel/2.6/drivers/net/can/Makefile (.../svn+ssh://chebla...@v5cvs/socketcan/vendor/socketcan/1095) (revision 199)
+++ kernel/2.6/drivers/net/can/Makefile (working copy)
@@ -23,6 +23,7 @@
export CONFIG_CAN_SOFTING=m
export CONFIG_CAN_SOFTING_CS=m
export CONFIG_CAN_MCP251X=m
+export CONFIG_CAN_PLX_PCI=m
modules modules_install clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core