All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
To: Netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: SocketCAN Core Mailing List
	<socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org>,
	linux-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org,
	Thomas Wiedemann <Thomas.Wiedemann-8kCRUXUQ4vU@public.gmane.org>
Subject: [PATCH net-next-2.6] can/sja1000: driver for PEAK PCAN PCI/PCIe cards
Date: Thu, 08 Sep 2011 22:07:28 +0200	[thread overview]
Message-ID: <4E692080.9020801@grandegger.com> (raw)

This patch adds the "peak_pci" driver for the PCAN PCI/PCIe cards (1, 2, 3
or 4 channels) from PEAK Systems (http://www.peak-system.com).

Signed-off-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
---

I have tested this patch on my 2 channel PEAK PCI card. Thomas, or somebody
else with a PEAK PCAN PCI/PCIe card at hand, it would be nice if you could
test it on your 4 channel card and add your "Tested-by".

Thanks,

Wolfgang. 

 drivers/net/can/sja1000/Kconfig    |    7 +
 drivers/net/can/sja1000/Makefile   |    1 +
 drivers/net/can/sja1000/peak_pci.c |  293 ++++++++++++++++++++++++++++++++++++
 3 files changed, 301 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/sja1000/peak_pci.c

diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig
index 6fdc031..72b637d 100644
--- a/drivers/net/can/sja1000/Kconfig
+++ b/drivers/net/can/sja1000/Kconfig
@@ -37,6 +37,13 @@ config CAN_EMS_PCI
 	  CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
 	  (http://www.ems-wuensche.de).
 
+config CAN_PEAK_PCI
+	tristate "PEAK PCAN PCI/PCIe Cards"
+	depends on PCI
+	---help---
+	  This driver is for the PCAN PCI/PCIe cards (1, 2, 3 or 4 channels)
+	  from PEAK Systems (http://www.peak-system.com).
+
 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 2c591eb..428f5cf 100644
--- a/drivers/net/can/sja1000/Makefile
+++ b/drivers/net/can/sja1000/Makefile
@@ -8,6 +8,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_KVASER_PCI) += kvaser_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
 obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o
 obj-$(CONFIG_CAN_TSCAN1) += tscan1.o
 
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
new file mode 100644
index 0000000..23d865d
--- /dev/null
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
+ *
+ * Derived from the PCAN project file driver/src/pcan_pci.c:
+ *
+ * Copyright (C) 2001-2006  PEAK System-Technik 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/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sja1000.h"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI/PCIe cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define DRV_NAME  "peak_pci"
+
+struct peak_pci_chan {
+	void __iomem *cfg_base;	     /* Common for all channels */
+	struct net_device *next_dev; /* Chain of network devices */
+	u16 icr_mask;		     /* Interrupt mask for fast ack */
+};
+
+#define PEAK_PCI_CAN_CLOCK	(16000000 / 2)
+
+#define PEAK_PCI_CDR		(CDR_CBP | CDR_CLKOUT_MASK)
+#define PEAK_PCI_OCR		OCR_TX0_PUSHPULL
+
+/*
+ * Important PITA registers
+ */
+#define PITA_ICR		0x00	/* Interrupt control register */
+#define PITA_GPIOICR		0x18	/* GPIO interface control register */
+#define PITA_MISC		0x1C	/* Miscellaneous register */
+
+#define PEAK_PCI_CFG_SIZE	0x1000	/* Size of the config PCI bar */
+#define PEAK_PCI_CHAN_SIZE	0x0400	/* Size used by the channel */
+
+#define PEAK_PCI_VENDOR_ID	0x001C	/* The PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID	0x0001	/* for PCI / PCIe slot cards */
+#define PEAK_PCIE_DEVICE_ID	0x0002	/* for PCIExpress cards */
+
+static const u16 peak_pci_icr_masks[] = {0x02, 0x01, 0x40, 0x80};
+
+static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = {
+	{PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+	{PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(const struct sja1000_priv *priv, int port)
+{
+	return readb(priv->reg_base + (port << 2));
+}
+
+static void peak_pci_write_reg(const struct sja1000_priv *priv,
+			       int port, u8 val)
+{
+	writeb(val, priv->reg_base + (port << 2));
+}
+
+static void peak_pci_post_irq(const struct sja1000_priv *priv)
+{
+	struct peak_pci_chan *chan = priv->priv;
+	u16 icr;
+
+	/* Select and clear in PITA stored interrupt */
+	icr = readw(chan->cfg_base + PITA_ICR);
+	if (icr & chan->icr_mask)
+		writew(chan->icr_mask, chan->cfg_base + PITA_ICR);
+}
+
+static int __devinit peak_pci_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
+{
+	struct sja1000_priv *priv;
+	struct peak_pci_chan *chan;
+	struct net_device *dev, *dev0 = NULL;
+	void __iomem *cfg_base, *reg_base;
+	u16 sub_sys_id, icr;
+	int i, err, channels;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pci_request_regions(pdev, DRV_NAME);
+	if (err)
+		goto failure_disable_pci;
+
+	err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+	if (err)
+		goto failure_release_regions;
+
+	dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n",
+		pdev->vendor, pdev->device, sub_sys_id);
+
+	err = pci_write_config_word(pdev, 0x44, 0);
+	if (err)
+		goto failure_release_regions;
+
+	if (sub_sys_id >= 12)
+		channels = 4;
+	else if (sub_sys_id >= 10)
+		channels = 3;
+	else if (sub_sys_id >= 4)
+		channels = 2;
+	else
+		channels = 1;
+
+	cfg_base = pci_iomap(pdev, 0, PEAK_PCI_CFG_SIZE);
+	if (!cfg_base) {
+		dev_err(&pdev->dev, "failed to map PCI resource #0\n");
+		goto failure_release_regions;
+	}
+
+	reg_base = pci_iomap(pdev, 1, PEAK_PCI_CHAN_SIZE * channels);
+	if (!reg_base) {
+		dev_err(&pdev->dev, "failed to map PCI resource #1\n");
+		goto failure_unmap_cfg_base;
+	}
+
+	/* Set GPIO control register */
+	writew(0x0005, cfg_base + PITA_GPIOICR + 2);
+	/* Enable all channels of this card */
+	writeb(0x00, cfg_base + PITA_GPIOICR);
+	/* Toggle reset */
+	writeb(0x05, cfg_base + PITA_MISC + 3);
+	mdelay(5);
+	/* Leave parport mux mode */
+	writeb(0x04, cfg_base + PITA_MISC + 3);
+
+	icr = readw(cfg_base + PITA_ICR + 2);
+
+	for (i = 0; i < channels; i++) {
+		dev = alloc_sja1000dev(sizeof(struct peak_pci_chan));
+		if (!dev) {
+			err = -ENOMEM;
+			goto failure_remove_channels;
+		}
+
+		priv = netdev_priv(dev);
+		chan = priv->priv;
+
+		chan->cfg_base = cfg_base;
+		priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE;
+
+		priv->read_reg = peak_pci_read_reg;
+		priv->write_reg = peak_pci_write_reg;
+		priv->post_irq = peak_pci_post_irq;
+
+		priv->can.clock.freq = PEAK_PCI_CAN_CLOCK;
+		priv->ocr = PEAK_PCI_OCR;
+		priv->cdr = PEAK_PCI_CDR;
+		/* Neither a slave nor a single device distributes the clock */
+		if (channels == 1 || i > 0)
+			priv->cdr |= CDR_CLK_OFF;
+
+		/* Setup interrupt handling */
+		priv->irq_flags = IRQF_SHARED;
+		dev->irq = pdev->irq;
+
+		chan->icr_mask = peak_pci_icr_masks[i];
+		icr |= chan->icr_mask;
+
+		SET_NETDEV_DEV(dev, &pdev->dev);
+
+		err = register_sja1000dev(dev);
+		if (err) {
+			dev_err(&pdev->dev, "failed to register device\n");
+			free_sja1000dev(dev);
+			goto failure_remove_channels;
+		}
+
+		/* Create chain of SJA1000 devices */
+		if (i == 0)
+			dev0 = dev;
+		else
+			chan->next_dev = dev;
+
+		dev_info(&pdev->dev,
+			 "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n",
+			 dev->name, priv->reg_base, chan->cfg_base, dev->irq);
+	}
+
+	pci_set_drvdata(pdev, dev0);
+
+	/* Enable interrupts */
+	writew(icr, cfg_base + PITA_ICR + 2);
+
+	return 0;
+
+failure_remove_channels:
+	/* Disable interrupts */
+	writew(0x0, cfg_base + PITA_ICR + 2);
+
+	for (dev = dev0; dev; dev = chan->next_dev) {
+		unregister_sja1000dev(dev);
+		free_sja1000dev(dev);
+		priv = netdev_priv(dev);
+		chan = priv->priv;
+		dev = chan->next_dev;
+	}
+
+	pci_iounmap(pdev, reg_base);
+
+failure_unmap_cfg_base:
+	pci_iounmap(pdev, cfg_base);
+
+failure_release_regions:
+	pci_release_regions(pdev);
+
+failure_disable_pci:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void __devexit peak_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev); /* First device */
+	struct sja1000_priv *priv = netdev_priv(dev);
+	struct peak_pci_chan *chan = priv->priv;
+	void __iomem *cfg_base = chan->cfg_base;
+	void __iomem *reg_base = priv->reg_base;
+
+	/* Disable interrupts */
+	writew(0x0, cfg_base + PITA_ICR + 2);
+
+	/* Loop over all registered devices */
+	while (1) {
+		dev_info(&pdev->dev, "removing device %s\n", dev->name);
+		unregister_sja1000dev(dev);
+		free_sja1000dev(dev);
+		dev = chan->next_dev;
+		if (!dev)
+			break;
+		priv = netdev_priv(dev);
+		chan = priv->priv;
+	}
+
+	pci_iounmap(pdev, reg_base);
+	pci_iounmap(pdev, cfg_base);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+
+	pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver peak_pci_driver = {
+	.name = DRV_NAME,
+	.id_table = peak_pci_tbl,
+	.probe = peak_pci_probe,
+	.remove = __devexit_p(peak_pci_remove),
+};
+
+static int __init peak_pci_init(void)
+{
+	return pci_register_driver(&peak_pci_driver);
+}
+module_init(peak_pci_init);
+
+static void __exit peak_pci_exit(void)
+{
+	pci_unregister_driver(&peak_pci_driver);
+}
+module_exit(peak_pci_exit);
-- 
1.7.4.1

             reply	other threads:[~2011-09-08 20:07 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-08 20:07 Wolfgang Grandegger [this message]
     [not found] ` <4E692080.9020801-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
2011-09-09 14:42   ` [PATCH net-next-2.6] can/sja1000: driver for PEAK PCAN PCI/PCIe cards Oliver Hartkopp
     [not found]     ` <4E6A25EE.3000501-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
2011-09-09 15:20       ` Wolfgang Grandegger
2011-09-12 15:49         ` Oliver Hartkopp
     [not found]           ` <4E6E2A21.7010701-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
2011-09-13  7:38             ` Wolfgang Grandegger
2011-09-27 14:58               ` Thomas Wiedemann
2011-09-29  9:21               ` Thomas Wiedemann
     [not found]                 ` <OFA1D2B7AC.618D2DB3-ONC125791A.0032F517-C125791A.00335871-Npw8gZsewqCELgA04lAiVw@public.gmane.org>
2011-09-29 10:39                   ` Wolfgang Grandegger
     [not found]                     ` <4E844AFB.3020201-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
2011-09-29 11:46                       ` Antwort: " Thomas Wiedemann
     [not found]                         ` <OF72B7C0BE.EE4FAB88-ONC125791A.004029CC-C125791A.004097EF-Npw8gZsewqCELgA04lAiVw@public.gmane.org>
2011-09-29 11:54                           ` Wolfgang Grandegger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E692080.9020801@grandegger.com \
    --to=wg-5yr1bzd7o62+xt7jha+gda@public.gmane.org \
    --cc=Netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=Thomas.Wiedemann-8kCRUXUQ4vU@public.gmane.org \
    --cc=linux-g4cQ8AsIbFbL9ATBNaCtXw@public.gmane.org \
    --cc=socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.