All of lore.kernel.org
 help / color / mirror / Atom feed
* [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices
@ 2016-10-04  6:25 Wolfgang Grandegger
  2016-10-06 19:40 ` Philippe Gerum
  0 siblings, 1 reply; 4+ messages in thread
From: Wolfgang Grandegger @ 2016-10-04  6:25 UTC (permalink / raw)
  To: Xenomai; +Cc: Markus Plessing, Sebastian Haas

The driver follows now more closely the corresponding Linux driver
to simplify updates in the future.

Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
---
 ksrc/drivers/can/sja1000/rtcan_ems_pci.c      | 487 +++++++++++++++-----------
 ksrc/drivers/can/sja1000/rtcan_sja1000_regs.h |   1 +
 2 files changed, 281 insertions(+), 207 deletions(-)

diff --git a/ksrc/drivers/can/sja1000/rtcan_ems_pci.c b/ksrc/drivers/can/sja1000/rtcan_ems_pci.c
index 4a88ca6..868d236 100644
--- a/ksrc/drivers/can/sja1000/rtcan_ems_pci.c
+++ b/ksrc/drivers/can/sja1000/rtcan_ems_pci.c
@@ -1,28 +1,28 @@
 /*
- * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2007, 2016 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
  *
- * Register definitions and descriptions are taken from LinCAN 0.3.3.
+ * Derived from Linux CAN SJA1000 PCI driver "ems_pci".
  *
- * 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 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.
+ * 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.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include <rtdm/rtdm_driver.h>
 
@@ -40,23 +40,30 @@
 static char *ems_pci_board_name = "EMS-CPC-PCI";
 
 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
-MODULE_DESCRIPTION("RTCAN board driver for EMS CPC-PCI cards");
-MODULE_SUPPORTED_DEVICE("EMS CPC-PCI card CAN controller");
-MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RTCAN board driver for EMS CPC-PCI/PCIe/104P CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe/104P CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_V1_MAX_CHAN 2
+#define EMS_PCI_V2_MAX_CHAN 4
+#define EMS_PCI_MAX_CHAN    EMS_PCI_V2_MAX_CHAN
+
+struct ems_pci_card {
+	int version;
+	int channels;
 
-struct rtcan_ems_pci
-{
 	struct pci_dev *pci_dev;
-	struct rtcan_device *slave_dev;
-	int channel;
-	volatile void __iomem *base_addr;
-	volatile void __iomem *conf_addr;
+	struct rtcan_device *rtcan_dev[EMS_PCI_MAX_CHAN];
+
+	void __iomem *conf_addr;
+	void __iomem *base_addr;
 };
 
-#define EMS_PCI_MASTER 1 /* multi channel device, this device is master */
-#define EMS_PCI_SLAVE  2 /* multi channel device, this is slave */
+#define EMS_PCI_CAN_CLOCK (16000000 / 2)
 
 /*
+ * Register definitions and descriptions are from LinCAN 0.3.3.
+ *
  * PSB4610 PITA-2 bridge control registers
  */
 #define PITA2_ICR           0x00	/* Interrupt Control Register */
@@ -64,7 +71,17 @@ struct rtcan_ems_pci
 #define PITA2_ICR_INT0_EN   0x00020000	/* [RW] Enable INT0 */
 
 #define PITA2_MISC          0x1c	/* Miscellaneous Register */
-#define PITA2_MISC_CONFIG   0x04000000	/* Multiplexed Parallel_interface_model */
+#define PITA2_MISC_CONFIG   0x04000000	/* Multiplexed parallel interface */
+
+/*
+ * Register definitions for the PLX 9030
+ */
+#define PLX_ICSR            0x4c   /* Interrupt Control/Status register */
+#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
+#define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */
+#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
+#define PLX_ICSR_ENA_CLR    (PLX_ICSR_LINTI1_ENA | PLX_ICSR_PCIINT_ENA | \
+			     PLX_ICSR_LINTI1_CLR)
 
 /*
  * The board configuration is probably following:
@@ -72,9 +89,9 @@ struct rtcan_ems_pci
  * 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.
+ * This means normal output mode, push-pull and the correct polarity.
  */
-#define EMS_PCI_OCR_STD     0xda	/* Standard value: Pushpull */
+#define EMS_PCI_OCR         (SJA_OCR_TX0_PUSHPULL | SJA_OCR_TX1_PUSHPULL)
 
 /*
  * In the CDR register, you should set CBP to 1.
@@ -82,248 +99,304 @@ struct rtcan_ems_pci
  * (meaning direct oscillator output) because the second SJA1000 chip
  * is driven by the first one CLKOUT output.
  */
-#define EMS_PCI_CDR_MASTER  (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07)
-#define EMS_PCI_CDR_SLAVE   (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07 |	\
-			     SJA_CDR_CLK_OFF)
-#define EMS_PCI_CONF_SIZE   0x0100  /* Size of the config io-memory */
-#define EMS_PCI_PORT_START  0x0400  /* Start of the channel io-memory */
-#define EMS_PCI_PORT_SIZE   0x0200  /* Size of a channel io-memory */
-
-
-#define EMS_PCI_PORT_BYTES  0x4     /* Each register occupies 4 bytes */
-
-#define EMS_PCI_VENDOR_ID   0x110a  /* PCI device and vendor ID */
-#define EMS_PCI_DEVICE_ID   0x2104
-
-static struct pci_device_id ems_pci_tbl[] = {
-	{EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{ }
+#define EMS_PCI_CDR             (SJA_CDR_CBP | SJA_CDR_CLKOUT_MASK)
+
+#define EMS_PCI_V1_BASE_BAR     1
+#define EMS_PCI_V1_CONF_SIZE    4096 /* size of PITA control area */
+#define EMS_PCI_V2_BASE_BAR     2
+#define EMS_PCI_V2_CONF_SIZE    128 /* size of PLX control area */
+#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
+#define EMS_PCI_CAN_CTRL_SIZE   0x200 /* memory size for each controller */
+
+#define EMS_PCI_BASE_SIZE  4096 /* size of controller area */
+
+static const struct pci_device_id ems_pci_tbl[] = {
+	/* CPC-PCI v1 */
+	{PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,},
+	/* CPC-PCI v2 */
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4000},
+	/* CPC-104P v2 */
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4002},
+	{0,}
 };
-MODULE_DEVICE_TABLE (pci, ems_pci_tbl);
+MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
 
-#define EMS_PCI_CAN_SYS_CLOCK (16000000 / 2)
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_v1_readb(struct ems_pci_card *card, unsigned int port)
+{
+	return readb((void __iomem *)card->base_addr + (port * 4));
+}
 
-static u8 rtcan_ems_pci_read_reg(struct rtcan_device *dev, int port)
+static u8 ems_pci_v1_read_reg(struct rtcan_device *dev, int port)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-	return readb(board->base_addr + (port * EMS_PCI_PORT_BYTES));
+	return readb((void __iomem *)dev->base_addr + (port * 4));
 }
 
-static void rtcan_ems_pci_write_reg(struct rtcan_device *dev, int port, u8 data)
+static void ems_pci_v1_write_reg(struct rtcan_device *dev,
+				 int port, u8 val)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-	writeb(data, board->base_addr + (port * EMS_PCI_PORT_BYTES));
+	writeb(val, (void __iomem *)dev->base_addr + (port * 4));
 }
 
-static void rtcan_ems_pci_irq_ack(struct rtcan_device *dev)
+static void ems_pci_v1_post_irq(struct rtcan_device *dev)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
+	struct ems_pci_card *card = (struct ems_pci_card *)dev->board_priv;
 
+	/* reset int flag of pita */
 	writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
-	       board->conf_addr + PITA2_ICR);
+	       card->conf_addr + PITA2_ICR);
 }
 
-static void rtcan_ems_pci_del_chan(struct rtcan_device *dev,
-				   int init_step)
+static u8 ems_pci_v2_read_reg(struct rtcan_device *dev, int port)
 {
-	struct rtcan_ems_pci *board;
+	return readb((void __iomem *)dev->base_addr + port);
+}
 
-	if (!dev)
-		return;
+static void ems_pci_v2_write_reg(struct rtcan_device *dev,
+				 int port, u8 val)
+{
+	writeb(val, (void __iomem *)dev->base_addr + port);
+}
 
-	board = (struct rtcan_ems_pci *)dev->board_priv;
+static void ems_pci_v2_post_irq(struct rtcan_device *dev)
+{
+	struct ems_pci_card *card = (struct ems_pci_card *)dev->board_priv;
 
-	switch (init_step) {
-	case 0:			/* Full cleanup */
-		RTCAN_DBG("Removing %s %s device %s\n",
-			  ems_pci_board_name, dev->ctrl_name, dev->name);
-		rtcan_sja1000_unregister(dev);
-	case 5:
-	case 4:
-		iounmap((void *)board->base_addr);
-	case 3:
-		if (board->channel != EMS_PCI_SLAVE)
-			iounmap((void *)board->conf_addr);
-	case 2:
-		rtcan_dev_free(dev);
-	case 1:
-		break;
-	}
+	writel(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
 }
 
-static int rtcan_ems_pci_add_chan(struct pci_dev *pdev, int channel,
-				  struct rtcan_device **master_dev)
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pci_check_chan(struct rtcan_device *dev)
 {
-	struct rtcan_device *dev;
-	struct rtcan_sja1000 *chip;
-	struct rtcan_ems_pci *board;
-	unsigned long addr;
-	int err, init_step = 1;
+	struct rtcan_sja1000 *chip = (struct rtcan_sja1000 *)dev->priv;
+	unsigned char res;
 
-	dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000),
-			      sizeof(struct rtcan_ems_pci));
-	if (dev == NULL)
-		return -ENOMEM;
-	init_step = 2;
+	/* Make sure SJA1000 is in reset mode */
+	chip->write_reg(dev, SJA_MOD, 1);
 
-	chip = (struct rtcan_sja1000 *)dev->priv;
-	board = (struct rtcan_ems_pci *)dev->board_priv;
+	chip->write_reg(dev, SJA_CDR, SJA_CDR_CAN_MODE);
 
-	board->pci_dev = pdev;
-	board->channel = channel;
+	/* read reset-values */
+	res = chip->read_reg(dev, SJA_CDR);
 
-	if (channel != EMS_PCI_SLAVE) {
+	if (res == SJA_CDR_CAN_MODE)
+		return 1;
 
-		addr = pci_resource_start(pdev, 0);
-		board->conf_addr = ioremap(addr, EMS_PCI_CONF_SIZE);
-		if (board->conf_addr == 0) {
-			err = -ENODEV;
-			goto failure;
-		}
-		init_step = 3;
+	return 0;
+}
 
-		/* Configure PITA-2 parallel interface */
-		writel(PITA2_MISC_CONFIG, board->conf_addr + PITA2_MISC);
-		/* Enable interrupts from card */
-		writel(PITA2_ICR_INT0_EN, board->conf_addr + PITA2_ICR);
-	} else {
-		struct rtcan_ems_pci *master_board =
-			(struct rtcan_ems_pci *)(*master_dev)->board_priv;
-		master_board->slave_dev = dev;
-		board->conf_addr = master_board->conf_addr;
-	}
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+	struct ems_pci_card *card = pci_get_drvdata(pdev);
+	struct rtcan_device *dev;
+	int i = 0;
 
-	addr = pci_resource_start(pdev, 1) + EMS_PCI_PORT_START;
-	if (channel == EMS_PCI_SLAVE)
-		addr += EMS_PCI_PORT_SIZE;
+	for (i = 0; i < card->channels; i++) {
+		dev = card->rtcan_dev[i];
 
-	board->base_addr = ioremap(addr, EMS_PCI_PORT_SIZE);
-	if (board->base_addr == 0) {
-		err = -ENODEV;
-		goto failure;
+		if (!dev)
+			continue;
+
+		dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+		rtcan_sja1000_unregister(dev);
+		rtcan_dev_free(dev);
 	}
-	init_step = 4;
 
-	dev->board_name = ems_pci_board_name;
+	if (card->base_addr != NULL)
+		pci_iounmap(card->pci_dev, card->base_addr);
 
-	chip->read_reg = rtcan_ems_pci_read_reg;
-	chip->write_reg = rtcan_ems_pci_write_reg;
-	chip->irq_ack = rtcan_ems_pci_irq_ack;
+	if (card->conf_addr != NULL)
+		pci_iounmap(card->pci_dev, card->conf_addr);
 
-	/* Clock frequency in Hz */
-	dev->can_sys_clock = EMS_PCI_CAN_SYS_CLOCK;
+	kfree(card);
 
-	/* Output control register */
-	chip->ocr = EMS_PCI_OCR_STD;
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
 
-	/* Clock divider register */
-	if (channel == EMS_PCI_MASTER)
-		chip->cdr = EMS_PCI_CDR_MASTER;
-	else
-		chip->cdr = EMS_PCI_CDR_SLAVE;
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+	/* Request board reset */
+	writeb(0, card->base_addr);
+}
 
-	strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ);
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to RTCAN subsystem.
+ */
+static int ems_pci_add_card(struct pci_dev *pdev,
+			    const struct pci_device_id *ent)
+{
+	struct rtcan_sja1000 *chip;
+	struct rtcan_device *dev;
+	struct ems_pci_card *card;
+	int max_chan, conf_size, base_bar;
+	int err, i;
+
+	/* Enabling PCI device */
+	if (pci_enable_device(pdev) < 0) {
+		dev_err(&pdev->dev, "Enabling PCI device failed\n");
+		return -ENODEV;
+	}
 
-	/* Register and setup interrupt handling */
-	chip->irq_flags = RTDM_IRQTYPE_SHARED;
-	chip->irq_num = pdev->irq;
-	init_step = 5;
+	/* Allocating card structures to hold addresses, ... */
+	card = kzalloc(sizeof(*card), GFP_KERNEL);
+	if (card == NULL) {
+		pci_disable_device(pdev);
+		return -ENOMEM;
+	}
 
-	printk("%s: base_addr=%p conf_addr=%p irq=%d\n", RTCAN_DRV_NAME,
-	       board->base_addr, board->conf_addr, chip->irq_num);
+	pci_set_drvdata(pdev, card);
 
-	/* Register SJA1000 device */
-	err = rtcan_sja1000_register(dev);
-	if (err) {
-		printk(KERN_ERR
-		       "ERROR %d while trying to register SJA1000 device!\n",
-		       err);
-		goto failure;
-	}
+	card->pci_dev = pdev;
 
-	if (channel != EMS_PCI_SLAVE)
-		*master_dev = dev;
+	card->channels = 0;
 
-	return 0;
+	if (pdev->vendor == PCI_VENDOR_ID_PLX) {
+		card->version = 2; /* CPC-PCI v2 */
+		max_chan = EMS_PCI_V2_MAX_CHAN;
+		base_bar = EMS_PCI_V2_BASE_BAR;
+		conf_size = EMS_PCI_V2_CONF_SIZE;
+	} else {
+		card->version = 1; /* CPC-PCI v1 */
+		max_chan = EMS_PCI_V1_MAX_CHAN;
+		base_bar = EMS_PCI_V1_BASE_BAR;
+		conf_size = EMS_PCI_V1_CONF_SIZE;
+	}
 
-failure:
-	rtcan_ems_pci_del_chan(dev, init_step);
-	return err;
-}
+	/* Remap configuration space and controller memory area */
+	card->conf_addr = pci_iomap(pdev, 0, conf_size);
+	if (card->conf_addr == NULL) {
+		err = -ENOMEM;
+		goto failure_cleanup;
+	}
 
-static int ems_pci_init_one(struct pci_dev *pdev,
-			    const struct pci_device_id *ent)
-{
-	struct rtcan_device *master_dev = NULL;
-	int err;
+	card->base_addr = pci_iomap(pdev, base_bar, EMS_PCI_BASE_SIZE);
+	if (card->base_addr == NULL) {
+		err = -ENOMEM;
+		goto failure_cleanup;
+	}
 
-	RTCAN_DBG("%s: initializing device %04x:%04x\n",
-		  RTCAN_DRV_NAME,  pdev->vendor, pdev->device);
+	if (card->version == 1) {
+		/* Configure PITA-2 parallel interface (enable MUX) */
+		writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC);
+
+		/* Check for unique EMS CAN signature */
+		if (ems_pci_v1_readb(card, 0) != 0x55 ||
+		    ems_pci_v1_readb(card, 1) != 0xAA ||
+		    ems_pci_v1_readb(card, 2) != 0x01 ||
+		    ems_pci_v1_readb(card, 3) != 0xCB ||
+		    ems_pci_v1_readb(card, 4) != 0x11) {
+			dev_err(&pdev->dev,
+				"Not EMS Dr. Thomas Wuensche interface\n");
+			err = -ENODEV;
+			goto failure_cleanup;
+		}
+	}
 
-	if ((err = pci_enable_device (pdev)))
-		goto failure;
+	ems_pci_card_reset(card);
 
-	if ((err = pci_request_regions(pdev, RTCAN_DRV_NAME)))
-		goto failure;
+	for (i = 0; i < max_chan; i++) {
+		dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000), 0);
+		if (!dev) {
+			err = -ENOMEM;
+			goto failure_cleanup;
+		}
 
-	if ((err = pci_write_config_word(pdev, 0x04, 2)))
-		goto failure_cleanup;
+		strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ);
+		dev->board_name = ems_pci_board_name;
+		dev->board_priv = card;
+
+		card->rtcan_dev[i] = dev;
+		chip = card->rtcan_dev[i]->priv;
+		chip->irq_flags = RTDM_IRQTYPE_SHARED;
+		chip->irq_num = pdev->irq;
+
+		dev->base_addr = (unsigned long)card->base_addr +
+			EMS_PCI_CAN_BASE_OFFSET + (i * EMS_PCI_CAN_CTRL_SIZE);
+		if (card->version == 1) {
+			chip->read_reg  = ems_pci_v1_read_reg;
+			chip->write_reg = ems_pci_v1_write_reg;
+			chip->irq_ack = ems_pci_v1_post_irq;
+		} else {
+			chip->read_reg  = ems_pci_v2_read_reg;
+			chip->write_reg = ems_pci_v2_write_reg;
+			chip->irq_ack = ems_pci_v2_post_irq;
+		}
 
-	if ((err = rtcan_ems_pci_add_chan(pdev, EMS_PCI_MASTER,
-					  &master_dev)))
-		goto failure_cleanup;
-	if ((err = rtcan_ems_pci_add_chan(pdev, EMS_PCI_SLAVE,
-					  &master_dev)))
+		/* Check if channel is present */
+		if (ems_pci_check_chan(dev)) {
+			dev->can_sys_clock = EMS_PCI_CAN_CLOCK;
+			chip->ocr = EMS_PCI_OCR | SJA_OCR_MODE_NORMAL;
+			chip->cdr = EMS_PCI_CDR | SJA_CDR_CAN_MODE;
+
+			if (card->version == 1)
+				/* reset int flag of pita */
+				writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
+				       card->conf_addr + PITA2_ICR);
+			else
+				/* enable IRQ in PLX 9030 */
+				writel(PLX_ICSR_ENA_CLR,
+				       card->conf_addr + PLX_ICSR);
+
+			/* Register SJA1000 device */
+			err = rtcan_sja1000_register(dev);
+			if (err) {
+				dev_err(&pdev->dev, "Registering device failed "
+					"(err=%d)\n", err);
+				rtcan_dev_free(dev);
+				goto failure_cleanup;
+			}
+
+			card->channels++;
+
+			dev_info(&pdev->dev, "Channel #%d at 0x%p, irq %d "
+				 "registered as %s\n", i + 1,
+				 (void* __iomem)dev->base_addr, chip->irq_num,
+				 dev->name);
+		} else {
+			dev_err(&pdev->dev, "Channel #%d not detected\n",
+				i + 1);
+			rtcan_dev_free(dev);
+		}
+	}
+
+	if (!card->channels) {
+		err = -ENODEV;
 		goto failure_cleanup;
+	}
 
-	pci_set_drvdata(pdev, master_dev);
 	return 0;
 
 failure_cleanup:
-	if (master_dev)
-		rtcan_ems_pci_del_chan(master_dev, 0);
+	dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
 
-	pci_release_regions(pdev);
+	ems_pci_del_card(pdev);
 
-failure:
 	return err;
-
-}
-
-static void ems_pci_remove_one(struct pci_dev *pdev)
-{
-	struct rtcan_device *dev = pci_get_drvdata(pdev);
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-
-	/* Disable interrupts from card */
-	writel(0x0, board->conf_addr + PITA2_ICR);
-
-	if (board->slave_dev)
-		rtcan_ems_pci_del_chan(board->slave_dev, 0);
-	rtcan_ems_pci_del_chan(dev, 0);
-
-	pci_release_regions(pdev);
-	pci_disable_device(pdev);
-	pci_set_drvdata(pdev, NULL);
 }
 
-static struct pci_driver rtcan_ems_pci_driver = {
-	.name		= RTCAN_DRV_NAME,
-	.id_table	= ems_pci_tbl,
-	.probe		= ems_pci_init_one,
-	.remove		= ems_pci_remove_one,
+static struct pci_driver ems_pci_driver = {
+	.name = RTCAN_DRV_NAME,
+	.id_table = ems_pci_tbl,
+	.probe = ems_pci_add_card,
+	.remove = ems_pci_del_card,
 };
 
-static int __init rtcan_ems_pci_init(void)
+static int __init ems_pci_init(void)
 {
-	return pci_register_driver(&rtcan_ems_pci_driver);
+	return pci_register_driver(&ems_pci_driver);
 }
 
-static void __exit rtcan_ems_pci_exit(void)
+static void __exit ems_pci_exit(void)
 {
-	pci_unregister_driver(&rtcan_ems_pci_driver);
+	pci_unregister_driver(&ems_pci_driver);
 }
 
-module_init(rtcan_ems_pci_init);
-module_exit(rtcan_ems_pci_exit);
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
diff --git a/ksrc/drivers/can/sja1000/rtcan_sja1000_regs.h b/ksrc/drivers/can/sja1000/rtcan_sja1000_regs.h
index f02d23b..9f2f871 100644
--- a/ksrc/drivers/can/sja1000/rtcan_sja1000_regs.h
+++ b/ksrc/drivers/can/sja1000/rtcan_sja1000_regs.h
@@ -197,6 +197,7 @@ enum SJA1000_PELI_FIR {
 
 /* Clock divider register */
 enum SJA1000_PELI_CDR {
+    SJA_CDR_CLKOUT_MASK  = 0x07,
     SJA_CDR_CLK_OFF      = 1<<3, /* Clock off (CLKOUT pin)              */
     SJA_CDR_CBP          = 1<<6, /* CAN input comparator bypass         */
     SJA_CDR_CAN_MODE     = 1<<7  /* CAN mode: 1 = PeliCAN               */
-- 
1.9.1




^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices
  2016-10-04  6:25 [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices Wolfgang Grandegger
@ 2016-10-06 19:40 ` Philippe Gerum
  2016-10-28  7:25   ` Wolfgang Grandegger
  0 siblings, 1 reply; 4+ messages in thread
From: Philippe Gerum @ 2016-10-06 19:40 UTC (permalink / raw)
  To: Wolfgang Grandegger, Xenomai; +Cc: Markus Plessing, Sebastian Haas


Hi Wolfgang,

On 10/04/2016 08:25 AM, Wolfgang Grandegger wrote:
> The driver follows now more closely the corresponding Linux driver
> to simplify updates in the future.
> 

Any chance to have this forward ported to 3.x? Xenomai 2.6 has reached EOL.

-- 
Philippe.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices
  2016-10-06 19:40 ` Philippe Gerum
@ 2016-10-28  7:25   ` Wolfgang Grandegger
  2016-10-30 16:44     ` Philippe Gerum
  0 siblings, 1 reply; 4+ messages in thread
From: Wolfgang Grandegger @ 2016-10-28  7:25 UTC (permalink / raw)
  To: Philippe Gerum, Xenomai; +Cc: Markus Plessing, Sebastian Haas

Hi Philippe

Am 06.10.2016 um 21:40 schrieb Philippe Gerum:
> 
> Hi Wolfgang,
> 
> On 10/04/2016 08:25 AM, Wolfgang Grandegger wrote:
>> The driver follows now more closely the corresponding Linux driver
>> to simplify updates in the future.
>>
> 
> Any chance to have this forward ported to 3.x? Xenomai 2.6 has reached EOL.

Below is the patch for Xenomai-3. It's untested for the moment but the
changes compared with 2.6 seem not the be critical.

Wolfgang


>From 0fc42b023eb9f6f58ce3b2381a55cab74757c2a1 Mon Sep 17 00:00:00 2001
From: Wolfgang Grandegger <Wolfgang Grandegger>
Date: Fri, 28 Oct 2016 09:21:24 +0200
Subject: [PATCH] [PATCH] rtcan: ems_pci driver update to support more devices

The driver now also follows more closely the corresponding Linux
driver to simplify updates in the future.

Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
---
 kernel/drivers/can/sja1000/rtcan_ems_pci.c      | 487 ++++++++++++++----------
 kernel/drivers/can/sja1000/rtcan_sja1000_regs.h |   1 +
 2 files changed, 281 insertions(+), 207 deletions(-)

diff --git a/kernel/drivers/can/sja1000/rtcan_ems_pci.c b/kernel/drivers/can/sja1000/rtcan_ems_pci.c
index be4b704..e3c178f 100644
--- a/kernel/drivers/can/sja1000/rtcan_ems_pci.c
+++ b/kernel/drivers/can/sja1000/rtcan_ems_pci.c
@@ -1,28 +1,28 @@
 /*
- * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2007, 2016 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
  *
- * Register definitions and descriptions are taken from LinCAN 0.3.3.
+ * Derived from Linux CAN SJA1000 PCI driver "ems_pci".
  *
- * 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 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.
+ * 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.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include <rtdm/driver.h>
 
@@ -40,23 +40,30 @@
 static char *ems_pci_board_name = "EMS-CPC-PCI";
 
 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
-MODULE_DESCRIPTION("RTCAN board driver for EMS CPC-PCI cards");
-MODULE_SUPPORTED_DEVICE("EMS CPC-PCI card CAN controller");
-MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RTCAN board driver for EMS CPC-PCI/PCIe/104P CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe/104P CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_V1_MAX_CHAN 2
+#define EMS_PCI_V2_MAX_CHAN 4
+#define EMS_PCI_MAX_CHAN    EMS_PCI_V2_MAX_CHAN
+
+struct ems_pci_card {
+	int version;
+	int channels;
 
-struct rtcan_ems_pci
-{
 	struct pci_dev *pci_dev;
-	struct rtcan_device *slave_dev;
-	int channel;
-	volatile void __iomem *base_addr;
-	volatile void __iomem *conf_addr;
+	struct rtcan_device *rtcan_dev[EMS_PCI_MAX_CHAN];
+
+	void __iomem *conf_addr;
+	void __iomem *base_addr;
 };
 
-#define EMS_PCI_MASTER 1 /* multi channel device, this device is master */
-#define EMS_PCI_SLAVE  2 /* multi channel device, this is slave */
+#define EMS_PCI_CAN_CLOCK (16000000 / 2)
 
 /*
+ * Register definitions and descriptions are from LinCAN 0.3.3.
+ *
  * PSB4610 PITA-2 bridge control registers
  */
 #define PITA2_ICR           0x00	/* Interrupt Control Register */
@@ -64,7 +71,17 @@ struct rtcan_ems_pci
 #define PITA2_ICR_INT0_EN   0x00020000	/* [RW] Enable INT0 */
 
 #define PITA2_MISC          0x1c	/* Miscellaneous Register */
-#define PITA2_MISC_CONFIG   0x04000000	/* Multiplexed Parallel_interface_model */
+#define PITA2_MISC_CONFIG   0x04000000	/* Multiplexed parallel interface */
+
+/*
+ * Register definitions for the PLX 9030
+ */
+#define PLX_ICSR            0x4c   /* Interrupt Control/Status register */
+#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
+#define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */
+#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
+#define PLX_ICSR_ENA_CLR    (PLX_ICSR_LINTI1_ENA | PLX_ICSR_PCIINT_ENA | \
+			     PLX_ICSR_LINTI1_CLR)
 
 /*
  * The board configuration is probably following:
@@ -72,9 +89,9 @@ struct rtcan_ems_pci
  * 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.
+ * This means normal output mode, push-pull and the correct polarity.
  */
-#define EMS_PCI_OCR_STD     0xda	/* Standard value: Pushpull */
+#define EMS_PCI_OCR         (SJA_OCR_TX0_PUSHPULL | SJA_OCR_TX1_PUSHPULL)
 
 /*
  * In the CDR register, you should set CBP to 1.
@@ -82,252 +99,308 @@ struct rtcan_ems_pci
  * (meaning direct oscillator output) because the second SJA1000 chip
  * is driven by the first one CLKOUT output.
  */
-#define EMS_PCI_CDR_MASTER  (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07)
-#define EMS_PCI_CDR_SLAVE   (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07 |	\
-			     SJA_CDR_CLK_OFF)
-#define EMS_PCI_CONF_SIZE   0x0100  /* Size of the config io-memory */
-#define EMS_PCI_PORT_START  0x0400  /* Start of the channel io-memory */
-#define EMS_PCI_PORT_SIZE   0x0200  /* Size of a channel io-memory */
-
-
-#define EMS_PCI_PORT_BYTES  0x4     /* Each register occupies 4 bytes */
-
-#define EMS_PCI_VENDOR_ID   0x110a  /* PCI device and vendor ID */
-#define EMS_PCI_DEVICE_ID   0x2104
-
-static struct pci_device_id ems_pci_tbl[] = {
-	{EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{ }
+#define EMS_PCI_CDR             (SJA_CDR_CBP | SJA_CDR_CLKOUT_MASK)
+
+#define EMS_PCI_V1_BASE_BAR     1
+#define EMS_PCI_V1_CONF_SIZE    4096 /* size of PITA control area */
+#define EMS_PCI_V2_BASE_BAR     2
+#define EMS_PCI_V2_CONF_SIZE    128 /* size of PLX control area */
+#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
+#define EMS_PCI_CAN_CTRL_SIZE   0x200 /* memory size for each controller */
+
+#define EMS_PCI_BASE_SIZE  4096 /* size of controller area */
+
+static const struct pci_device_id ems_pci_tbl[] = {
+	/* CPC-PCI v1 */
+	{PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,},
+	/* CPC-PCI v2 */
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4000},
+	/* CPC-104P v2 */
+	{PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4002},
+	{0,}
 };
-MODULE_DEVICE_TABLE (pci, ems_pci_tbl);
+MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
 
-#define EMS_PCI_CAN_SYS_CLOCK (16000000 / 2)
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_v1_readb(struct ems_pci_card *card, unsigned int port)
+{
+	return readb((void __iomem *)card->base_addr + (port * 4));
+}
 
-static u8 rtcan_ems_pci_read_reg(struct rtcan_device *dev, int port)
+static u8 ems_pci_v1_read_reg(struct rtcan_device *dev, int port)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-	return readb(board->base_addr + (port * EMS_PCI_PORT_BYTES));
+	return readb((void __iomem *)dev->base_addr + (port * 4));
 }
 
-static void rtcan_ems_pci_write_reg(struct rtcan_device *dev, int port, u8 data)
+static void ems_pci_v1_write_reg(struct rtcan_device *dev,
+				 int port, u8 val)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-	writeb(data, board->base_addr + (port * EMS_PCI_PORT_BYTES));
+	writeb(val, (void __iomem *)dev->base_addr + (port * 4));
 }
 
-static void rtcan_ems_pci_irq_ack(struct rtcan_device *dev)
+static void ems_pci_v1_post_irq(struct rtcan_device *dev)
 {
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
+	struct ems_pci_card *card = (struct ems_pci_card *)dev->board_priv;
 
+	/* reset int flag of pita */
 	writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
-	       board->conf_addr + PITA2_ICR);
+	       card->conf_addr + PITA2_ICR);
 }
 
-static void rtcan_ems_pci_del_chan(struct rtcan_device *dev,
-				   int init_step)
+static u8 ems_pci_v2_read_reg(struct rtcan_device *dev, int port)
 {
-	struct rtcan_ems_pci *board;
+	return readb((void __iomem *)dev->base_addr + port);
+}
 
-	if (!dev)
-		return;
+static void ems_pci_v2_write_reg(struct rtcan_device *dev,
+				 int port, u8 val)
+{
+	writeb(val, (void __iomem *)dev->base_addr + port);
+}
 
-	board = (struct rtcan_ems_pci *)dev->board_priv;
+static void ems_pci_v2_post_irq(struct rtcan_device *dev)
+{
+	struct ems_pci_card *card = (struct ems_pci_card *)dev->board_priv;
 
-	switch (init_step) {
-	case 0:			/* Full cleanup */
-		RTCAN_DBG("Removing %s %s device %s\n",
-			  ems_pci_board_name, dev->ctrl_name, dev->name);
-		rtcan_sja1000_unregister(dev);
-	case 5:
-	case 4:
-		iounmap((void *)board->base_addr);
-	case 3:
-		if (board->channel != EMS_PCI_SLAVE)
-			iounmap((void *)board->conf_addr);
-	case 2:
-		rtcan_dev_free(dev);
-	case 1:
-		break;
-	}
+	writel(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
 }
 
-static int rtcan_ems_pci_add_chan(struct pci_dev *pdev, int channel,
-				  struct rtcan_device **master_dev)
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pci_check_chan(struct rtcan_device *dev)
 {
-	struct rtcan_device *dev;
-	struct rtcan_sja1000 *chip;
-	struct rtcan_ems_pci *board;
-	unsigned long addr;
-	int err, init_step = 1;
+	struct rtcan_sja1000 *chip = (struct rtcan_sja1000 *)dev->priv;
+	unsigned char res;
 
-	dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000),
-			      sizeof(struct rtcan_ems_pci));
-	if (dev == NULL)
-		return -ENOMEM;
-	init_step = 2;
+	/* Make sure SJA1000 is in reset mode */
+	chip->write_reg(dev, SJA_MOD, 1);
 
-	chip = (struct rtcan_sja1000 *)dev->priv;
-	board = (struct rtcan_ems_pci *)dev->board_priv;
+	chip->write_reg(dev, SJA_CDR, SJA_CDR_CAN_MODE);
 
-	board->pci_dev = pdev;
-	board->channel = channel;
+	/* read reset-values */
+	res = chip->read_reg(dev, SJA_CDR);
 
-	if (channel != EMS_PCI_SLAVE) {
+	if (res == SJA_CDR_CAN_MODE)
+		return 1;
 
-		addr = pci_resource_start(pdev, 0);
-		board->conf_addr = ioremap(addr, EMS_PCI_CONF_SIZE);
-		if (board->conf_addr == 0) {
-			err = -ENODEV;
-			goto failure;
-		}
-		init_step = 3;
+	return 0;
+}
 
-		/* Configure PITA-2 parallel interface */
-		writel(PITA2_MISC_CONFIG, board->conf_addr + PITA2_MISC);
-		/* Enable interrupts from card */
-		writel(PITA2_ICR_INT0_EN, board->conf_addr + PITA2_ICR);
-	} else {
-		struct rtcan_ems_pci *master_board =
-			(struct rtcan_ems_pci *)(*master_dev)->board_priv;
-		master_board->slave_dev = dev;
-		board->conf_addr = master_board->conf_addr;
-	}
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+	struct ems_pci_card *card = pci_get_drvdata(pdev);
+	struct rtcan_device *dev;
+	int i = 0;
 
-	addr = pci_resource_start(pdev, 1) + EMS_PCI_PORT_START;
-	if (channel == EMS_PCI_SLAVE)
-		addr += EMS_PCI_PORT_SIZE;
+	for (i = 0; i < card->channels; i++) {
+		dev = card->rtcan_dev[i];
 
-	board->base_addr = ioremap(addr, EMS_PCI_PORT_SIZE);
-	if (board->base_addr == 0) {
-		err = -ENODEV;
-		goto failure;
+		if (!dev)
+			continue;
+
+		dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+		rtcan_sja1000_unregister(dev);
+		rtcan_dev_free(dev);
 	}
-	init_step = 4;
 
-	dev->board_name = ems_pci_board_name;
+	if (card->base_addr != NULL)
+		pci_iounmap(card->pci_dev, card->base_addr);
 
-	chip->read_reg = rtcan_ems_pci_read_reg;
-	chip->write_reg = rtcan_ems_pci_write_reg;
-	chip->irq_ack = rtcan_ems_pci_irq_ack;
+	if (card->conf_addr != NULL)
+		pci_iounmap(card->pci_dev, card->conf_addr);
 
-	/* Clock frequency in Hz */
-	dev->can_sys_clock = EMS_PCI_CAN_SYS_CLOCK;
+	kfree(card);
 
-	/* Output control register */
-	chip->ocr = EMS_PCI_OCR_STD;
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
 
-	/* Clock divider register */
-	if (channel == EMS_PCI_MASTER)
-		chip->cdr = EMS_PCI_CDR_MASTER;
-	else
-		chip->cdr = EMS_PCI_CDR_SLAVE;
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+	/* Request board reset */
+	writeb(0, card->base_addr);
+}
 
-	strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ);
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to RTCAN subsystem.
+ */
+static int ems_pci_add_card(struct pci_dev *pdev,
+			    const struct pci_device_id *ent)
+{
+	struct rtcan_sja1000 *chip;
+	struct rtcan_device *dev;
+	struct ems_pci_card *card;
+	int max_chan, conf_size, base_bar;
+	int err, i;
+
+	/* Enabling PCI device */
+	if (pci_enable_device(pdev) < 0) {
+		dev_err(&pdev->dev, "Enabling PCI device failed\n");
+		return -ENODEV;
+	}
 
-	/* Register and setup interrupt handling */
-	chip->irq_flags = RTDM_IRQTYPE_SHARED;
-	chip->irq_num = pdev->irq;
-	init_step = 5;
+	/* Allocating card structures to hold addresses, ... */
+	card = kzalloc(sizeof(*card), GFP_KERNEL);
+	if (card == NULL) {
+		pci_disable_device(pdev);
+		return -ENOMEM;
+	}
 
-	printk("%s: base_addr=%p conf_addr=%p irq=%d\n", RTCAN_DRV_NAME,
-	       board->base_addr, board->conf_addr, chip->irq_num);
+	pci_set_drvdata(pdev, card);
 
-	/* Register SJA1000 device */
-	err = rtcan_sja1000_register(dev);
-	if (err) {
-		printk(KERN_ERR
-		       "ERROR %d while trying to register SJA1000 device!\n",
-		       err);
-		goto failure;
-	}
+	card->pci_dev = pdev;
 
-	if (channel != EMS_PCI_SLAVE)
-		*master_dev = dev;
+	card->channels = 0;
 
-	return 0;
+	if (pdev->vendor == PCI_VENDOR_ID_PLX) {
+		card->version = 2; /* CPC-PCI v2 */
+		max_chan = EMS_PCI_V2_MAX_CHAN;
+		base_bar = EMS_PCI_V2_BASE_BAR;
+		conf_size = EMS_PCI_V2_CONF_SIZE;
+	} else {
+		card->version = 1; /* CPC-PCI v1 */
+		max_chan = EMS_PCI_V1_MAX_CHAN;
+		base_bar = EMS_PCI_V1_BASE_BAR;
+		conf_size = EMS_PCI_V1_CONF_SIZE;
+	}
 
-failure:
-	rtcan_ems_pci_del_chan(dev, init_step);
-	return err;
-}
+	/* Remap configuration space and controller memory area */
+	card->conf_addr = pci_iomap(pdev, 0, conf_size);
+	if (card->conf_addr == NULL) {
+		err = -ENOMEM;
+		goto failure_cleanup;
+	}
 
-static int ems_pci_init_one(struct pci_dev *pdev,
-			    const struct pci_device_id *ent)
-{
-	struct rtcan_device *master_dev = NULL;
-	int err;
+	card->base_addr = pci_iomap(pdev, base_bar, EMS_PCI_BASE_SIZE);
+	if (card->base_addr == NULL) {
+		err = -ENOMEM;
+		goto failure_cleanup;
+	}
 
-	RTCAN_DBG("%s: initializing device %04x:%04x\n",
-		  RTCAN_DRV_NAME,  pdev->vendor, pdev->device);
+	if (card->version == 1) {
+		/* Configure PITA-2 parallel interface (enable MUX) */
+		writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC);
+
+		/* Check for unique EMS CAN signature */
+		if (ems_pci_v1_readb(card, 0) != 0x55 ||
+		    ems_pci_v1_readb(card, 1) != 0xAA ||
+		    ems_pci_v1_readb(card, 2) != 0x01 ||
+		    ems_pci_v1_readb(card, 3) != 0xCB ||
+		    ems_pci_v1_readb(card, 4) != 0x11) {
+			dev_err(&pdev->dev,
+				"Not EMS Dr. Thomas Wuensche interface\n");
+			err = -ENODEV;
+			goto failure_cleanup;
+		}
+	}
 
-	if ((err = pci_enable_device (pdev)))
-		goto failure;
+	ems_pci_card_reset(card);
 
-	if ((err = pci_request_regions(pdev, RTCAN_DRV_NAME)))
-		goto failure;
+	for (i = 0; i < max_chan; i++) {
+		dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000), 0);
+		if (!dev) {
+			err = -ENOMEM;
+			goto failure_cleanup;
+		}
 
-	if ((err = pci_write_config_word(pdev, 0x04, 2)))
-		goto failure_cleanup;
+		strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ);
+		dev->board_name = ems_pci_board_name;
+		dev->board_priv = card;
+
+		card->rtcan_dev[i] = dev;
+		chip = card->rtcan_dev[i]->priv;
+		chip->irq_flags = RTDM_IRQTYPE_SHARED;
+		chip->irq_num = pdev->irq;
+
+		dev->base_addr = (unsigned long)card->base_addr +
+			EMS_PCI_CAN_BASE_OFFSET + (i * EMS_PCI_CAN_CTRL_SIZE);
+		if (card->version == 1) {
+			chip->read_reg  = ems_pci_v1_read_reg;
+			chip->write_reg = ems_pci_v1_write_reg;
+			chip->irq_ack = ems_pci_v1_post_irq;
+		} else {
+			chip->read_reg  = ems_pci_v2_read_reg;
+			chip->write_reg = ems_pci_v2_write_reg;
+			chip->irq_ack = ems_pci_v2_post_irq;
+		}
 
-	if ((err = rtcan_ems_pci_add_chan(pdev, EMS_PCI_MASTER,
-					  &master_dev)))
-		goto failure_cleanup;
-	if ((err = rtcan_ems_pci_add_chan(pdev, EMS_PCI_SLAVE,
-					  &master_dev)))
+		/* Check if channel is present */
+		if (ems_pci_check_chan(dev)) {
+			dev->can_sys_clock = EMS_PCI_CAN_CLOCK;
+			chip->ocr = EMS_PCI_OCR | SJA_OCR_MODE_NORMAL;
+			chip->cdr = EMS_PCI_CDR | SJA_CDR_CAN_MODE;
+
+			if (card->version == 1)
+				/* reset int flag of pita */
+				writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
+				       card->conf_addr + PITA2_ICR);
+			else
+				/* enable IRQ in PLX 9030 */
+				writel(PLX_ICSR_ENA_CLR,
+				       card->conf_addr + PLX_ICSR);
+
+			/* Register SJA1000 device */
+			err = rtcan_sja1000_register(dev);
+			if (err) {
+				dev_err(&pdev->dev, "Registering device failed "
+					"(err=%d)\n", err);
+				rtcan_dev_free(dev);
+				goto failure_cleanup;
+			}
+
+			card->channels++;
+
+			dev_info(&pdev->dev, "Channel #%d at 0x%p, irq %d "
+				 "registered as %s\n", i + 1,
+				 (void* __iomem)dev->base_addr, chip->irq_num,
+				 dev->name);
+		} else {
+			dev_err(&pdev->dev, "Channel #%d not detected\n",
+				i + 1);
+			rtcan_dev_free(dev);
+		}
+	}
+
+	if (!card->channels) {
+		err = -ENODEV;
 		goto failure_cleanup;
+	}
 
-	pci_set_drvdata(pdev, master_dev);
 	return 0;
 
 failure_cleanup:
-	if (master_dev)
-		rtcan_ems_pci_del_chan(master_dev, 0);
+	dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
 
-	pci_release_regions(pdev);
+	ems_pci_del_card(pdev);
 
-failure:
 	return err;
-
-}
-
-static void ems_pci_remove_one(struct pci_dev *pdev)
-{
-	struct rtcan_device *dev = pci_get_drvdata(pdev);
-	struct rtcan_ems_pci *board = (struct rtcan_ems_pci *)dev->board_priv;
-
-	/* Disable interrupts from card */
-	writel(0x0, board->conf_addr + PITA2_ICR);
-
-	if (board->slave_dev)
-		rtcan_ems_pci_del_chan(board->slave_dev, 0);
-	rtcan_ems_pci_del_chan(dev, 0);
-
-	pci_release_regions(pdev);
-	pci_disable_device(pdev);
-	pci_set_drvdata(pdev, NULL);
 }
 
-static struct pci_driver rtcan_ems_pci_driver = {
-	.name		= RTCAN_DRV_NAME,
-	.id_table	= ems_pci_tbl,
-	.probe		= ems_pci_init_one,
-	.remove		= ems_pci_remove_one,
+static struct pci_driver ems_pci_driver = {
+	.name = RTCAN_DRV_NAME,
+	.id_table = ems_pci_tbl,
+	.probe = ems_pci_add_card,
+	.remove = ems_pci_del_card,
 };
 
-static int __init rtcan_ems_pci_init(void)
+static int __init ems_pci_init(void)
 {
 	if (!realtime_core_enabled())
 		return 0;
 
-	return pci_register_driver(&rtcan_ems_pci_driver);
+	return pci_register_driver(&ems_pci_driver);
 }
 
-static void __exit rtcan_ems_pci_exit(void)
+static void __exit ems_pci_exit(void)
 {
 	if (realtime_core_enabled())
-		pci_unregister_driver(&rtcan_ems_pci_driver);
+		pci_unregister_driver(&ems_pci_driver);
 }
 
-module_init(rtcan_ems_pci_init);
-module_exit(rtcan_ems_pci_exit);
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
diff --git a/kernel/drivers/can/sja1000/rtcan_sja1000_regs.h b/kernel/drivers/can/sja1000/rtcan_sja1000_regs.h
index f02d23b..9f2f871 100644
--- a/kernel/drivers/can/sja1000/rtcan_sja1000_regs.h
+++ b/kernel/drivers/can/sja1000/rtcan_sja1000_regs.h
@@ -197,6 +197,7 @@ enum SJA1000_PELI_FIR {
 
 /* Clock divider register */
 enum SJA1000_PELI_CDR {
+    SJA_CDR_CLKOUT_MASK  = 0x07,
     SJA_CDR_CLK_OFF      = 1<<3, /* Clock off (CLKOUT pin)              */
     SJA_CDR_CBP          = 1<<6, /* CAN input comparator bypass         */
     SJA_CDR_CAN_MODE     = 1<<7  /* CAN mode: 1 = PeliCAN               */
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices
  2016-10-28  7:25   ` Wolfgang Grandegger
@ 2016-10-30 16:44     ` Philippe Gerum
  0 siblings, 0 replies; 4+ messages in thread
From: Philippe Gerum @ 2016-10-30 16:44 UTC (permalink / raw)
  To: Wolfgang Grandegger, Xenomai; +Cc: Markus Plessing, Sebastian Haas


Hi Wolfgang,

On 10/28/2016 09:25 AM, Wolfgang Grandegger wrote:
> Hi Philippe
> 
> Am 06.10.2016 um 21:40 schrieb Philippe Gerum:
>>
>> Hi Wolfgang,
>>
>> On 10/04/2016 08:25 AM, Wolfgang Grandegger wrote:
>>> The driver follows now more closely the corresponding Linux driver
>>> to simplify updates in the future.
>>>
>>
>> Any chance to have this forward ported to 3.x? Xenomai 2.6 has reached EOL.
> 
> Below is the patch for Xenomai-3. It's untested for the moment but the
> changes compared with 2.6 seem not the be critical.
> 

Merged, thanks.

-- 
Philippe.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-10-30 16:44 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-04  6:25 [Xenomai] [PATCH] rtcan: ems_pci driver update to support more devices Wolfgang Grandegger
2016-10-06 19:40 ` Philippe Gerum
2016-10-28  7:25   ` Wolfgang Grandegger
2016-10-30 16:44     ` Philippe Gerum

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.