--- /dev/null
+#
+# $Id: Makefile 443 2007-07-25 11:41:27Z hartkopp $
+#
+# Copyright (c) 2002-2005 Volkswagen Group Electronic Research
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, the following disclaimer and
+# the referenced file 'COPYING'.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Volkswagen nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# Alternatively, provided that this notice is retained in full, this
+# software may be distributed under the terms of the GNU General
+# Public License ("GPL") version 2 as distributed in the 'COPYING'
+# file from the main directory of the linux kernel source.
+#
+# The provided data structures and external interfaces from this code
+# are not restricted to be used by modules with a GPL compatible license.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+# Send feedback to <socketcan-users@lists.berlios.de>
+
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+#EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/hal
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000.o
+obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
+obj-$(CONFIG_CAN_IXXAT_PCI) += ixxat_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
+obj-$(CONFIG_CAN_PCM027) += pcm027.o
+obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
+
+endif
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "can-ems-pci"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_MAX_CHAN 2
+
+struct ems_pci_card {
+ int channels;
+
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev[EMS_PCI_MAX_CHAN];
+
+ void __iomem *conf_addr;
+ void __iomem *base_addr;
+};
+
+#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 */
+#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */
+#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */
+
+#define PITA2_MISC 0x1c /* Miscellaneous Register */
+#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
+
+/*
+ * 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 EMS_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 EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCI_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#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_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,}
+};
+MODULE_DEVICE_TABLE (pci, ems_pci_tbl);
+
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_readb(struct ems_pci_card *card, unsigned int port)
+{
+ return readb((void __iomem *)card->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static u8 ems_pci_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct ems_pci_card *card = (struct ems_pci_card *)priv->priv;
+
+ /* reset int flag of pita */
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, card->conf_addr
+ + PITA2_ICR);
+}
+
+/*
+ * 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 net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pci_write_reg(dev, REG_MOD, 1);
+
+ ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pci_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+ struct ems_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 != NULL )
+ pci_iounmap(card->pci_dev, card->base_addr);
+
+ if (card->conf_addr != NULL)
+ pci_iounmap(card->pci_dev, card->conf_addr);
+
+ kfree(card);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+ /* Request board reset */
+ writeb(0, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pci_add_card(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pci_card *card;
+ int err, i;
+
+ /* Enabling PCI device */
+ if (pci_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "Enabling PCI device failed\n");
+ return -ENODEV;
+ }
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_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 PITA configuration space, and controller memory area */
+ card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE);
+ if (card->conf_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ /* 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_readb(card, 0) != 0x55 ||
+ ems_pci_readb(card, 1) != 0xAA ||
+ ems_pci_readb(card, 2) != 0x01 ||
+ ems_pci_readb(card, 3) != 0xCB ||
+ ems_pci_readb(card, 4) != 0x11) {
+ dev_err(&pdev->dev, "Not EMS Dr. Thomas Wuensche interface\n");
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pci_card_reset(card);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCI_MAX_CHAN; 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;
+
+ dev->irq = pdev->irq;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCI_CAN_BASE_OFFSET
+ + (i * EMS_PCI_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pci_check_chan(dev)) {
+ priv->read_reg = ems_pci_read_reg;
+ priv->write_reg = ems_pci_write_reg;
+ priv->post_irq = ems_pci_post_irq;
+ priv->can.can_sys_clock = EMS_PCI_CAN_CLOCK;
+ priv->ocr = EMS_PCI_OCR;
+ priv->cdr = EMS_PCI_CDR;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Enable interrupts from card */
+ writel(PITA2_ICR_INT0_EN, card->conf_addr + PITA2_ICR);
+
+ /* 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 %lX, irq %d\n",
+ i + 1, dev->base_addr,
+ dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ return 0;
+
+failure_cleanup:
+ dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
+
+ ems_pci_del_card(pdev);
+
+ return err;
+}
+
+static struct pci_driver ems_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ems_pci_tbl,
+ .probe = ems_pci_add_card,
+ .remove = ems_pci_del_card,
+};
+
+static int __init ems_pci_init(void)
+{
+ return pci_register_driver(&ems_pci_driver);
+}
+
+static void __exit ems_pci_exit(void)
+{
+ pci_unregister_driver(&ems_pci_driver);
+}
+
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
+
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "can-ixxat-pci"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Socket-CAN driver for IXXAT PC-I 04/PCI PCI cards");
+MODULE_SUPPORTED_DEVICE("IXXAT PC-I 04/PCI card");
+MODULE_LICENSE("GPL v2");
+
+/* Maximum number of interfaces supported on one card. Currently
+ * we only support a maximum of two interfaces, which is the maximum
+ * of what Ixxat sells anyway.
+ */
+#define IXXAT_PCI_MAX_CAN 2
+
+struct ixxat_pci {
+ struct pci_dev *pci_dev;
+ struct net_device *dev[IXXAT_PCI_MAX_CAN];
+ int conf_addr;
+ void __iomem *base_addr;
+};
+
+#define IXXAT_PCI_CAN_CLOCK (16000000 / 2)
+
+#define IXXAT_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX0_INVERT | \
+ OCR_TX1_PUSHPULL)
+#define IXXAT_PCI_CDR 0
+
+#define CHANNEL_RESET_OFFSET 0x110
+#define CHANNEL_OFFSET 0x200
+
+#define INTCSR_OFFSET 0x4c /* Offset in PLX9050 conf registers */
+#define INTCSR_LINTI1 (1 << 0)
+#define INTCSR_LINTI2 (1 << 3)
+#define INTCSR_PCI (1 << 6)
+
+/* PCI vender, device and sub-device ID */
+#define IXXAT_PCI_VENDOR_ID 0x10b5
+#define IXXAT_PCI_DEVICE_ID 0x9050
+#define IXXAT_PCI_SUB_SYS_ID 0x2540
+
+#define IXXAT_PCI_BASE_SIZE 0x400
+
+static struct pci_device_id ixxat_pci_tbl[] = {
+ {IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ixxat_pci_tbl);
+
+static u8 ixxat_pci_read_reg(struct net_device *ndev, int port)
+{
+ u8 val;
+ val = readb((void __iomem *)(ndev->base_addr + port));
+ return val;
+}
+
+static void ixxat_pci_write_reg(struct net_device *ndev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)(ndev->base_addr + port));
+}
+
+static void ixxat_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 *ixxat_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);
+
+ ndev->base_addr = (unsigned long)base_addr;
+
+ priv->read_reg = ixxat_pci_read_reg;
+ priv->write_reg = ixxat_pci_write_reg;
+
+ priv->can.can_sys_clock = IXXAT_PCI_CAN_CLOCK;
+
+ priv->ocr = IXXAT_PCI_OCR;
+ priv->cdr = IXXAT_PCI_CDR;
+
+ /* Set and enable PCI interrupts */
+ ndev->irq = pdev->irq;
+
+ dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
+ ndev->base_addr, 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 ixxat_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct ixxat_pci *board;
+ int err, intcsr = INTCSR_LINTI1 | INTCSR_PCI;
+ u16 sub_sys_id;
+ void __iomem *base_addr;
+
+ dev_info(&pdev->dev, "Initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+
+ if ((err = pci_enable_device(pdev)))
+ goto failure;
+
+ if ((err = pci_request_regions(pdev, DRV_NAME)))
+ goto failure;
+
+ if ((err = pci_read_config_word(pdev, 0x2e, &sub_sys_id)))
+ goto failure_release_pci;
+
+ if (sub_sys_id != IXXAT_PCI_SUB_SYS_ID)
+ return -ENODEV;
+
+ /* Enable memory and I/O space */
+ if ((err = pci_write_config_word(pdev, 0x04, 0x3)))
+ goto failure_release_pci;
+
+ board->conf_addr = pci_resource_start(pdev, 1);
+
+ base_addr = pci_iomap(pdev, 2, IXXAT_PCI_BASE_SIZE);
+ if (base_addr == 0) {
+ err = -ENODEV;
+ goto failure_release_pci;
+ }
+
+ board->base_addr = base_addr;
+
+ writeb(0x1, base_addr + CHANNEL_RESET_OFFSET);
+ writeb(0x1, base_addr + CHANNEL_OFFSET + CHANNEL_RESET_OFFSET);
+ udelay(100);
+
+ board->dev[0] = ixxat_pci_add_chan(pdev, base_addr);
+ if (IS_ERR(board->dev[0]))
+ goto failure_iounmap;
+
+ /* Check if second channel is available */
+ if (readb(base_addr + CHANNEL_OFFSET + REG_MOD) == 0x21 &&
+ readb(base_addr + CHANNEL_OFFSET + REG_SR) == 0x0c &&
+ readb(base_addr + CHANNEL_OFFSET + REG_IR) == 0xe0) {
+ board->dev[1] = ixxat_pci_add_chan(pdev,
+ base_addr + CHANNEL_OFFSET);
+ if (IS_ERR(board->dev[1]))
+ goto failure_unreg_dev0;
+
+ intcsr |= INTCSR_LINTI2;
+ }
+
+ /* enable interrupt(s) in PLX9050 */
+ outb(intcsr, board->conf_addr + INTCSR_OFFSET);
+
+ pci_set_drvdata(pdev, board);
+
+ return 0;
+
+failure_unreg_dev0:
+ ixxat_pci_del_chan(pdev, board->dev[0]);
+
+failure_iounmap:
+ pci_iounmap(pdev, board->base_addr);
+
+failure_release_pci:
+ pci_release_regions(pdev);
+
+failure:
+ kfree(board);
+
+ return err;
+}
+
+static void __devexit ixxat_pci_remove_one(struct pci_dev *pdev)
+{
+ struct ixxat_pci *board = pci_get_drvdata(pdev);
+ int i;
+
+ /* Disable interrupts in PLX9050*/
+ outb(0, board->conf_addr + INTCSR_OFFSET);
+
+ for (i = 0; i < IXXAT_PCI_MAX_CAN; i++) {
+ if (!board->dev[i])
+ break;
+ ixxat_pci_del_chan(pdev, board->dev[i]);
+ }
+
+ pci_iounmap(pdev, board->base_addr);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(board);
+}
+
+static struct pci_driver ixxat_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ixxat_pci_tbl,
+ .probe = ixxat_pci_init_one,
+ .remove = __devexit_p(ixxat_pci_remove_one),
+};
+
+static int __init ixxat_pci_init(void)
+{
+ return pci_register_driver(&ixxat_pci_driver);
+}
+
+static void __exit ixxat_pci_exit(void)
+{
+ pci_unregister_driver(&ixxat_pci_driver);
+}
+
+module_init(ixxat_pci_init);
+module_exit(ixxat_pci_exit);
--- /dev/null
+/*
+ * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se>
+ *
+ * Parts of this software are based on (derived) the following:
+ *
+ * - Kvaser linux driver, version 4.72 BETA
+ * Copyright (C) 2002-2007 KVASER AB
+ *
+ * - Lincan driver, version 0.3.3, OCERA project
+ * Copyright (C) 2004 Pavel Pisa
+ * Copyright (C) 2001 Arnaud Westenberg
+ *
+ * - Socketcan SJA1000 drivers
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "can-kvaser-pci"
+
+MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>");
+MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define MAX_NO_OF_CHANNELS 4 /* max no of channels on
+ a single card */
+
+struct kvaser_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1];
+ void __iomem *conf_addr;
+ void __iomem *res_addr;
+ int no_channels;
+ u8 xilinx_ver;
+};
+
+#define KVASER_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * 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 KVASER_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 0
+ * (meaning divide-by-2), the Pelican bit, and the clock-off bit
+ * (you will have no need for CLKOUT anyway).
+ */
+#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+
+/*
+ * These register values are valid for revision 14 of the Xilinx logic.
+ */
+#define XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define XILINX_PRESUMED_VERSION 14
+
+/*
+ * Important S5920 registers
+ */
+#define S5920_INTCSR 0x38
+#define S5920_PTCR 0x60
+#define INTCSR_ADDON_INTENABLE_M 0x2000
+
+
+#define KVASER_PCI_PORT_BYTES 0x20
+
+#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */
+#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */
+
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID1 0x8406
+
+#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID2 0x0008
+
+static struct pci_device_id kvaser_pci_tbl[] = {
+ {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,},
+ {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl);
+
+static u8 kvaser_pci_read_reg(struct net_device *dev, int port)
+{
+ return ioread8((void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_disable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Disable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp &= ~INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static void kvaser_pci_enable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Enable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp |= INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static int number_of_sja1000_chip(void __iomem *base_addr)
+{
+ u8 status;
+ int i;
+
+ for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
+ /* reset chip */
+ iowrite8(MOD_RM, base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ status = ioread8(base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ udelay(10);
+ /* check reset bit */
+ if (!(status & MOD_RM))
+ break;
+ }
+
+ return i;
+}
+
+static void kvaser_pci_del_chan(struct net_device *dev)
+{
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int i;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ dev->name);
+
+ for (i = 0; i < board->no_channels - 1; i++) {
+ if (board->slave_dev[i]) {
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ board->slave_dev[i]->name);
+ unregister_sja1000dev(board->slave_dev[i]);
+ free_sja1000dev(board->slave_dev[i]);
+ }
+ }
+ unregister_sja1000dev(dev);
+
+ /* Disable PCI interrupts */
+ kvaser_pci_disable_irq(dev);
+
+ pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr);
+ pci_iounmap(board->pci_dev, board->conf_addr);
+ pci_iounmap(board->pci_dev, board->res_addr);
+
+ free_sja1000dev(dev);
+}
+
+static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev,
+ void __iomem *conf_addr,
+ void __iomem *res_addr,
+ unsigned long base_addr)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ /*S5920*/
+ board->conf_addr = conf_addr;
+
+ /*XILINX board wide address*/
+ board->res_addr = res_addr;
+
+ if (channel == 0) {
+ board->xilinx_ver =
+ ioread8(board->res_addr + XILINX_VERINT) >> 4;
+ init_step = 2;
+
+ /* Assert PTADR# - we're in passive mode so the other bits are
+ not important */
+ iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR);
+
+ /* Disable interrupts from card */
+ kvaser_pci_disable_irq(dev);
+ /* Enable interrupts from card */
+ kvaser_pci_enable_irq(dev);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct kvaser_pci *master_board = master_priv->priv;
+ master_board->slave_dev[channel - 1] = dev;
+ master_board->no_channels = channel + 1;
+ board->xilinx_ver = master_board->xilinx_ver;
+ }
+
+ dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES;
+
+ priv->read_reg = kvaser_pci_read_reg;
+ priv->write_reg = kvaser_pci_write_reg;
+
+ priv->can.can_sys_clock = KVASER_PCI_CAN_CLOCK;
+
+ priv->ocr = KVASER_PCI_OCR;
+ priv->cdr = KVASER_PCI_CDR;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ init_step = 4;
+
+ dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n",
+ dev->base_addr, board->conf_addr, dev->irq);
+
+ 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);
+ goto failure;
+ }
+
+ if (channel == 0)
+ *master_dev = dev;
+
+ return 0;
+
+failure:
+ kvaser_pci_del_chan(dev);
+ return err;
+}
+
+static int __devinit kvaser_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ struct net_device *master_dev = NULL;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int no_channels;
+ void __iomem *base_addr = NULL;
+ void __iomem *conf_addr = NULL;
+ void __iomem *res_addr = NULL;
+ int i;
+
+ dev_info(&pdev->dev, "initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure_release_pci;
+
+ /*S5920*/
+ conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE);
+ if (conf_addr == 0) {
+ err = -ENODEV;
+ goto failure_iounmap;
+ }
+
+ /*XILINX board wide address*/
+ res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE);
+ if (res_addr == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE);
+ if (base_addr == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ no_channels = number_of_sja1000_chip(base_addr);
+ if (no_channels == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ for (i = 0; i < no_channels; i++) {
+ err = kvaser_pci_add_chan(pdev, i, &master_dev,
+ conf_addr, res_addr,
+ (unsigned long)base_addr);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ priv = netdev_priv(master_dev);
+ board = priv->priv;
+
+ dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n",
+ board->xilinx_ver, board->no_channels);
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ kvaser_pci_del_chan(master_dev);
+
+failure_iounmap:
+ if (conf_addr == 0)
+ pci_iounmap(pdev, conf_addr);
+ if (res_addr == 0)
+ pci_iounmap(pdev, res_addr);
+ if (base_addr == 0)
+ pci_iounmap(pdev, base_addr);
+
+ pci_release_regions(pdev);
+
+failure_release_pci:
+ pci_disable_device(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ kvaser_pci_del_chan(dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver kvaser_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = kvaser_pci_tbl,
+ .probe = kvaser_pci_init_one,
+ .remove = __devexit_p(kvaser_pci_remove_one),
+};
+
+static int __init kvaser_pci_init(void)
+{
+ return pci_register_driver(&kvaser_pci_driver);
+}
+
+static void __exit kvaser_pci_exit(void)
+{
+ pci_unregister_driver(&kvaser_pci_driver);
+}
+
+module_init(kvaser_pci_init);
+module_exit(kvaser_pci_exit);
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Copyright (C) 2005 Sascha Hauer, Pengutronix
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include <asm/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "can-pcm027"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("Socket-CAN driver for Phytec PCM027 board");
+MODULE_SUPPORTED_DEVICE("Phytec PCM027 board");
+MODULE_LICENSE("GPL v2");
+
+#define PCM027_CAN_CLOCK (16000000 / 2)
+
+#define PCM027_OCR (OCR_TX1_PULLDOWN | OCR_TX0_PUSHPULL)
+#define PCM027_CDR CDR_CBP
+
+static u8 pcm027_read_reg(struct net_device *dev, int reg)
+{
+ return ioread8(dev->base_addr + reg);
+}
+
+static void pcm027_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ iowrite8(val, dev->base_addr + reg);
+}
+
+static int pcm027_probe(struct platform_device *pdev)
+{
+ int err, irq;
+ void __iomem *addr = 0;
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res;
+
+ err = -ENODEV;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || !irq)
+ goto exit;
+
+ err = -EBUSY;
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ DRV_NAME)) {
+ goto exit;
+ }
+
+ err = -ENOMEM;
+ addr = ioremap_nocache(res->start, res->end - res->start + 1);
+ if (!addr) {
+ goto exit_release;
+ }
+
+ dev = alloc_sja1000dev(0);
+ if (!dev)
+ goto exit_iounmap;
+
+ priv = netdev_priv(dev);
+
+ priv->read_reg = pcm027_read_reg;
+ priv->write_reg = pcm027_write_reg;
+ priv->can.can_sys_clock = PCM027_CAN_CLOCK;
+ priv->ocr = PCM027_OCR;
+ priv->cdr = PCM027_CDR;
+
+ dev->irq = irq;
+ dev->base_addr = (unsigned long)addr;
+
+ dev_set_drvdata(&pdev->dev, dev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free;
+ }
+
+ printk("%s: %s device registered (base_addr=%#lx, irq=%d)\n",
+ dev->name, DRV_NAME, dev->base_addr, dev->irq);
+ return 0;
+
+exit_free:
+ free_sja1000dev(dev);
+exit_iounmap:
+ iounmap(addr);
+exit_release:
+ release_mem_region(res->start, res->end - res->start + 1);
+exit:
+ return err;
+}
+
+static int pcm027_remove (struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ unregister_sja1000dev(dev);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ if (dev->base_addr)
+ iounmap ((void *)dev->base_addr);
+
+ free_sja1000dev(dev);
+
+ return 0;
+}
+
+static int pcm027_suspend (struct platform_device *pdev,
+ pm_message_t state)
+{
+ dev_err(&pdev->dev, "suspend not implented\n");
+ return 0;
+}
+
+
+static int pcm027_resume (struct platform_device *pdev)
+{
+ dev_err(&pdev->dev, "resume not implemented\n");
+ return 0;
+}
+
+static struct platform_driver pcm027_driver = {
+ .probe = pcm027_probe,
+ .remove = pcm027_remove,
+ .suspend = pcm027_suspend,
+ .resume = pcm027_resume,
+ .driver = {
+ .name = "pcm027can",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pcm027_init(void)
+{
+ return platform_driver_register(&pcm027_driver);
+}
+
+static void __exit pcm027_exit(void)
+{
+ platform_driver_unregister(&pcm027_driver);
+}
+
+module_init(pcm027_init);
+module_exit(pcm027_exit);
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "can-peak-pci"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+struct peak_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev;
+ volatile void __iomem *conf_addr;
+};
+
+#define PEAK_PCI_SINGLE 0 /* single channel device */
+#define PEAK_PCI_MASTER 1 /* multi channel master device */
+#define PEAK_PCI_SLAVE 2 /* multi channel slave device */
+
+#define PEAK_PCI_CAN_CLOCK (16000000 / 2)
+
+#define PEAK_PCI_CDR_SINGLE (CDR_CBP | CDR_CLKOUT_MASK | CDR_CLK_OFF)
+#define PEAK_PCI_CDR_MASTER (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 /* general purpose I/O interface
+ control register */
+#define PITA_MISC 0x1C /* miscellanoes register */
+
+#define PCI_CONFIG_PORT_SIZE 0x1000 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x0400 /* size of a channel io-memory */
+
+#define PEAK_PCI_VENDOR_ID 0x001C /* the PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID 0x0001
+
+static struct pci_device_id peak_pci_tbl[] = {
+ {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(struct net_device *dev, int port)
+{
+ u8 val;
+ val = readb((const volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+ return val;
+}
+
+static void peak_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+}
+
+static void peak_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+ u16 icr_low;
+
+ /* Select and clear in Pita stored interrupt */
+ icr_low = readw(board->conf_addr + PITA_ICR);
+ if (board->channel == PEAK_PCI_SLAVE) {
+ if (icr_low & 0x0001)
+ writew(0x0001, board->conf_addr + PITA_ICR);
+ } else {
+ if (icr_low & 0x0002)
+ writew(0x0002, board->conf_addr + PITA_ICR);
+ }
+}
+
+static void peak_pci_del_chan(struct net_device *dev, int init_step)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board;
+ u16 icr_high;
+
+ if (!dev || !(priv = netdev_priv(dev)) || !(board = priv->priv))
+ return;
+
+ switch (init_step) {
+ case 0: /* Full cleanup */
+ printk("Removing %s device %s\n", DRV_NAME, dev->name);
+ unregister_sja1000dev(dev);
+ case 4:
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (board->channel == PEAK_PCI_SLAVE) {
+ icr_high &= ~0x0001;
+ } else {
+ icr_high &= ~0x0002;
+ }
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ case 3:
+ iounmap((void *)dev->base_addr);
+ case 2:
+ if (board->channel != PEAK_PCI_SLAVE)
+ iounmap((void *)board->conf_addr);
+ case 1:
+ free_sja1000dev(dev);
+ break;
+ }
+
+}
+
+static int peak_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct peak_pci *board;
+ u16 icr_high;
+ unsigned long addr;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct peak_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+ init_step = 1;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ if (channel != PEAK_PCI_SLAVE) {
+
+ addr = pci_resource_start(pdev, 0);
+ board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE);
+ if (board->conf_addr == 0) {
+ err = -ENODEV;
+ goto failure;
+ }
+ init_step = 2;
+
+ /* Set GPIO control register */
+ writew(0x0005, board->conf_addr + PITA_GPIOICR + 2);
+
+ /* Enable single or dual channel */
+ if (channel == PEAK_PCI_MASTER)
+ writeb(0x00, board->conf_addr + PITA_GPIOICR);
+ else
+ writeb(0x04, board->conf_addr + PITA_GPIOICR);
+ /* Toggle reset */
+ writeb(0x05, board->conf_addr + PITA_MISC + 3);
+ mdelay(5);
+ /* Leave parport mux mode */
+ writeb(0x04, board->conf_addr + PITA_MISC + 3);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct peak_pci *master_board = master_priv->priv;
+ master_board->slave_dev = dev;
+ board->conf_addr = master_board->conf_addr;
+ }
+
+ addr = pci_resource_start(pdev, 1);
+ if (channel == PEAK_PCI_SLAVE)
+ addr += PCI_PORT_SIZE;
+
+ dev->base_addr = (unsigned long)ioremap(addr, PCI_PORT_SIZE);
+ if (dev->base_addr == 0) {
+ err = -ENOMEM;
+ goto failure;
+ }
+ init_step = 3;
+
+ priv->read_reg = peak_pci_read_reg;
+ priv->write_reg = peak_pci_write_reg;
+ priv->post_irq = peak_pci_post_irq;
+
+ priv->can.can_sys_clock = PEAK_PCI_CAN_CLOCK;
+
+ priv->ocr = PEAK_PCI_OCR;
+
+ if (channel == PEAK_PCI_MASTER)
+ priv->cdr = PEAK_PCI_CDR_MASTER;
+ else
+ priv->cdr = PEAK_PCI_CDR_SINGLE;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (channel == PEAK_PCI_SLAVE) {
+ icr_high |= 0x0001;
+ } else {
+ icr_high |= 0x0002;
+ }
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ init_step = 4;
+
+ printk("%s: base_addr=%#lx conf_addr=%p irq=%d\n", DRV_NAME,
+ dev->base_addr, board->conf_addr, dev->irq);
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_ERR "Registering %s device failed (err=%d)\n",
+ DRV_NAME, err);
+ goto failure;
+ }
+
+ if (channel != PEAK_PCI_SLAVE)
+ *master_dev = dev;
+
+ return 0;
+
+failure:
+ peak_pci_del_chan(dev, init_step);
+ return err;
+}
+
+static int __devinit peak_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ u16 sub_sys_id;
+ struct net_device *master_dev = NULL;
+
+ printk("%s: initializing device %04x:%04x\n",
+ DRV_NAME, pdev->vendor, pdev->device);
+
+ if ((err = pci_enable_device(pdev)))
+ goto failure;
+
+ if ((err = pci_request_regions(pdev, DRV_NAME)))
+ goto failure;
+
+ if ((err = pci_read_config_word(pdev, 0x2e, &sub_sys_id)))
+ goto failure_cleanup;
+
+ if ((err = pci_write_config_word(pdev, 0x44, 0)))
+ goto failure_cleanup;
+
+ if (sub_sys_id > 3) {
+ if ((err = peak_pci_add_chan(pdev, PEAK_PCI_MASTER,
+ &master_dev)))
+ goto failure_cleanup;
+ if ((err = peak_pci_add_chan(pdev, PEAK_PCI_SLAVE,
+ &master_dev)))
+ goto failure_cleanup;
+ } else {
+ if ((err = peak_pci_add_chan(pdev, PEAK_PCI_SINGLE,
+ &master_dev)))
+ goto failure_cleanup;
+ }
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ if (master_dev)
+ peak_pci_del_chan(master_dev, 0);
+
+ pci_release_regions(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit peak_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+
+ if (board->slave_dev)
+ peak_pci_del_chan(board->slave_dev, 0);
+ peak_pci_del_chan(dev, 0);
+
+ 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_init_one,
+ .remove = __devexit_p(peak_pci_remove_one),
+};
+
+static int __init peak_pci_init(void)
+{
+ return pci_register_driver(&peak_pci_driver);
+}
+
+static void __exit peak_pci_exit(void)
+{
+ pci_unregister_driver(&peak_pci_driver);
+}
+
+module_init(peak_pci_init);
+module_exit(peak_pci_exit);
--- /dev/null
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/dev.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+
+#include "sja1000.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id: sja1000.c 531 2007-10-19 07:38:29Z hartkopp $");
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Socketcan " CHIP_NAME " network device driver");
+
+#define CONFIG_CAN_DEBUG_DEVICES
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+/* driver and version information */
+static const char *drv_name = "SJA1000";
+static const char *drv_version = "0.2.0";
+static const char *drv_reldate = "2007-10-25";
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+static int debug = 0;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug, "Set debug mask (default: 0)");
+
+
+static int sja1000_probe_chip(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ if (dev->base_addr && (priv->read_reg(dev, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ drv_name, dev->base_addr);
+ return 0;
+ }
+ return 1;
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ /* disable interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+ priv->can.state = CAN_STATE_STOPPED;
+ return 0;
+ }
+
+ priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n");
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ priv->can.state = CAN_STATE_ACTIVE;
+ /* enable all interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_ALL);
+
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ priv->write_reg(dev, REG_MOD, 0x00);
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n");
+ return 1;
+
+}
+
+static void sja1000_start(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* Clear error counters and error code capture */
+ priv->write_reg(dev, REG_TXERR, 0x0);
+ priv->write_reg(dev, REG_RXERR, 0x0);
+ priv->read_reg(dev, REG_ECC);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int sja1000_set_mode(struct net_device *dev, can_mode_t mode)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ DBG("%s: CAN_MODE_START requested\n", __FUNCTION__);
+ if (!priv->open_time)
+ return -EINVAL;
+
+ sja1000_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sja1000_get_state(struct net_device *dev, can_state_t *state)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ u8 status;
+
+ /* FIXME: inspecting the status register to get the current state
+ * is not really necessary, because state changes are handled by
+ * in the ISR and the variable priv->can.state gets updated. The
+ * CAN devicde interface needs fixing!
+ */
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ *state = CAN_STATE_STOPPED;
+ } else {
+ status = priv->read_reg(dev, REG_SR);
+ if (status & SR_BS)
+ *state = CAN_STATE_BUS_OFF;
+ else if (status & SR_ES) {
+ if (priv->read_reg(dev, REG_TXERR) > 127 ||
+ priv->read_reg(dev, REG_RXERR) > 127 )
+ *state = CAN_STATE_BUS_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_WARNING;
+ }
+ else
+ *state = CAN_STATE_ACTIVE;
+ }
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ /* Check state */
+ if (*state != priv->can.state)
+ dev_err(ND2D(dev),
+ "Oops, state mismatch: hard %d != soft %d\n",
+ *state, priv->can.state);
+#endif
+ spin_unlock_irq(&priv->can.irq_lock);
+ return 0;
+}
+
+static int sja1000_set_bittime(struct net_device *dev, struct can_bittime *bt)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ u8 btr0, btr1;
+
+ switch (bt->type) {
+ case CAN_BITTIME_BTR:
+ btr0 = bt->btr.btr0;
+ btr1 = bt->btr.btr1;
+ break;
+
+ case CAN_BITTIME_STD:
+ btr0 = ((bt->std.brp - 1) & 0x3f) |
+ (((bt->std.sjw - 1) & 0x3) << 6);
+ btr1 = ((bt->std.prop_seg +
+ bt->std.phase_seg1 - 1) & 0xf) |
+ (((bt->std.phase_seg2 - 1) & 0x7) << 4) |
+ ((bt->std.sam & 1) << 7);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ DBG("%s: BTR0 0x%02x BTR1 0x%02x\n", dev->name, btr0, btr1);
+
+ priv->write_reg(dev, REG_BTR0, btr0);
+ priv->write_reg(dev, REG_BTR1, btr1);
+
+ return 0;
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* set clock divider and output control register */
+ priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN);
+
+ /* set acceptance filter (accept all) */
+ priv->write_reg(dev, REG_ACCC0, 0x00);
+ priv->write_reg(dev, REG_ACCC1, 0x00);
+ priv->write_reg(dev, REG_ACCC2, 0x00);
+ priv->write_reg(dev, REG_ACCC3, 0x00);
+
+ priv->write_reg(dev, REG_ACCM0, 0xFF);
+ priv->write_reg(dev, REG_ACCM1, 0xFF);
+ priv->write_reg(dev, REG_ACCM2, 0xFF);
+ priv->write_reg(dev, REG_ACCM3, 0xFF);
+
+ priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5);
+ priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3);
+ priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++) {
+ priv->write_reg(dev, dreg++, cf->data[i]);
+ }
+
+ stats->tx_bytes += dlc;
+ dev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, dev, 0);
+
+ priv->write_reg(dev, REG_CMR, CMD_TR);
+
+ return 0;
+}
+
+static void sja1000_rx(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = priv->read_reg(dev, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << (5 + 16))
+ | (priv->read_reg(dev, REG_ID2) << (5 + 8))
+ | (priv->read_reg(dev, REG_ID3) << 5)
+ | (priv->read_reg(dev, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << 3)
+ | (priv->read_reg(dev, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = priv->read_reg(dev, dreg++);
+ }
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ priv->write_reg(dev, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+static int sja1000_err(struct net_device *dev,
+ uint8_t isrc, uint8_t status, int n)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ can_state_t state = priv->can.state;
+ uint8_t ecc, alc;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ priv->can.can_stats.data_overrun++;
+ priv->write_reg(dev, REG_CMR, CMD_CDO); /* clear bit */
+ }
+
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can.can_stats.error_warning++;
+
+ if (status & SR_BS) {
+ state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(dev);
+ iDBG(KERN_INFO "%s: BUS OFF\n", dev->name);
+ } else if (status & SR_ES) {
+ state = CAN_STATE_BUS_WARNING;
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ } else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can.can_stats.bus_error);
+ priv->can.can_stats.bus_error++;
+ ecc = priv->read_reg(dev, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & ECC_MASK) {
+ case ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & ECC_SEG;
+ break;
+ }
+ /* Error occured during transmission? */
+ if ((ecc & ECC_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can.can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: ERROR PASSIVE\n", dev->name);
+ state = CAN_STATE_BUS_PASSIVE;
+ } else {
+ iDBG(KERN_INFO "%s: ERROR ACTIVE\n", dev->name);
+ state = CAN_STATE_ACTIVE;
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ alc = priv->read_reg(dev, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ priv->can.can_stats.arbitration_lost++;
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = alc & 0x1f;
+ }
+
+ if (state != priv->can.state && (state == CAN_STATE_BUS_WARNING ||
+ state == CAN_STATE_BUS_PASSIVE)) {
+ uint8_t rxerr = priv->read_reg(dev, REG_RXERR);
+ uint8_t txerr = priv->read_reg(dev, REG_TXERR);
+ cf->can_id |= CAN_ERR_CRTL;
+ if (state == CAN_STATE_BUS_WARNING)
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ else
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+
+ priv->can.state = state;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t sja1000_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+#else
+static irqreturn_t sja1000_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ uint8_t isrc, status;
+ int n = 0;
+
+ if (priv->pre_irq)
+ priv->pre_irq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __FUNCTION__, priv->read_reg(dev, REG_MOD),
+ priv->read_reg(dev, REG_IER), priv->read_reg(dev, REG_IR),
+ priv->read_reg(dev, REG_SR));
+ goto out;
+ }
+
+ while ((isrc = priv->read_reg(dev, REG_IR)) && (n < 20)) {
+ n++;
+ status = priv->read_reg(dev, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can.can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+ while (status & SR_RBS) {
+ sja1000_rx(dev);
+ status = priv->read_reg(dev, REG_SR);
+ }
+ }
+ if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
+ /* error interrupt */
+ if (sja1000_err(dev, isrc, status, n))
+ break;
+ }
+ }
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+
+out:
+ if (priv->post_irq)
+ priv->post_irq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int sja1000_open(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* register interrupt handler */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ err = request_irq(dev->irq, &sja1000_interrupt, SA_SHIRQ,
+ dev->name, (void *)dev);
+#else
+ err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED,
+ dev->name, (void *)dev);
+#endif
+ if (err)
+ return -EAGAIN;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* clear statistics */
+ memset(&priv->can.net_stats, 0, sizeof(priv->can.net_stats));
+#endif
+
+ /* init and start chi */
+ sja1000_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int sja1000_close(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ set_reset_mode(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+ can_close_cleanup(dev);
+ free_irq(dev->irq, (void *)dev);
+
+ return 0;
+}
+
+struct net_device *alloc_sja1000dev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+
+ dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct sja1000_priv);
+
+ return dev;
+}
+
+EXPORT_SYMBOL_GPL(alloc_sja1000dev);
+
+void free_sja1000dev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL(free_sja1000dev);
+
+int register_sja1000dev(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!sja1000_probe_chip(dev))
+ return -ENODEV;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ dev->open = sja1000_open;
+ dev->stop = sja1000_close;
+
+ dev->hard_start_xmit = sja1000_start_xmit;
+
+ priv->can.do_set_bittime = sja1000_set_bittime;
+ priv->can.do_get_state = sja1000_get_state;
+ priv->can.do_set_mode = sja1000_set_mode;
+ priv->dev = dev;
+
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_INFO
+ "%s: registering netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return err;
+ }
+
+ set_reset_mode(dev);
+ chipset_init(dev);
+ return 0;
+}
+EXPORT_SYMBOL(register_sja1000dev);
+
+void unregister_sja1000dev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_sja1000dev);
+
+static __init int sja1000_init(void)
+{
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [debug %d]\n", drv_name, debug);
+
+ printk("%s driver %s %s loaded\n", drv_name, drv_version, drv_reldate);
+ return 0;
+}
+
+module_init(sja1000_init);
+
+static __exit void sja1000_exit(void)
+{
+ printk("%s driver %s %s unloaded\n",
+ drv_name, drv_version, drv_reldate);
+}
+
+module_exit(sja1000_exit);
--- /dev/null
+/*
+ * $Id: sja1000.h 505 2007-09-30 13:32:41Z hartkopp $
+ *
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000DEV_H
+#define SJA1000DEV_H
+
+#include <linux/can/dev.h>
+
+#define CHIP_NAME "SJA1000"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG 0x1F
+#define ECC_DIR 0x20
+#define ECC_ERR 6
+#define ECC_BIT 0x00
+#define ECC_FORM 0x40
+#define ECC_STUFF 0x80
+#define ECC_MASK 0xc0
+
+/* clock divider register */
+#define CDR_CLKOUT_MASK 0x07
+#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */
+#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */
+#define CDR_CBP 0x40 /* CAN input comparator bypass */
+#define CDR_PELICAN 0x80 /* PeliCAN mode */
+
+/* output control register */
+#define OCR_MODE_BIPHASE 0x00
+#define OCR_MODE_TEST 0x01
+#define OCR_MODE_NORMAL 0x02
+#define OCR_MODE_CLOCK 0x03
+#define OCR_TX0_INVERT 0x04
+#define OCR_TX0_PULLDOWN 0x08
+#define OCR_TX0_PULLUP 0x10
+#define OCR_TX0_PUSHPULL 0x18
+#define OCR_TX1_INVERT 0x20
+#define OCR_TX1_PULLDOWN 0x40
+#define OCR_TX1_PULLUP 0x80
+#define OCR_TX1_PUSHPULL 0xc0
+
+/*
+ * SJA1000 private data structure
+ */
+struct sja1000_priv {
+ struct can_priv can; /* must be the first member! */
+ long open_time;
+ struct sk_buff *echo_skb;
+ u8 (*read_reg) (struct net_device * dev, int reg);
+ void (*write_reg) (struct net_device * dev, int reg, u8 val);
+ void (*pre_irq) (struct net_device * dev);
+ void (*post_irq) (struct net_device * dev);
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+ u8 ocr;
+ u8 cdr;
+};
+
+struct net_device *alloc_sja1000dev(int sizeof_priv);
+void free_sja1000dev(struct net_device *dev);
+int register_sja1000dev(struct net_device *dev);
+void unregister_sja1000dev(struct net_device *dev);
+
+#if 0
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+#endif
+
+#endif /* SJA1000_DEV_H */
--- /dev/null
+/*
+ * $Id: dev.c 542 2007-11-07 13:57:16Z thuermann $
+ *
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sysfs.h"
+
+#ifdef CONFIG_SYSFS
+/*
+ * Functions to set/get CAN properties used by SYSFS
+ *
+ * FIXME: we may want to check for capabilities!
+ *
+ * if (!capable(CAP_NET_ADMIN))
+ * return -EPERM;
+ */
+static int can_get_bitrate(struct net_device *dev, u32 *bitrate)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ *bitrate = priv->bitrate;
+
+ return 0;
+}
+
+static int can_set_custombittime(struct net_device *dev,
+ struct can_bittime *bt)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err = -ENOTSUPP;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ if (priv->do_set_bittime) {
+ err = priv->do_set_bittime(dev, bt);
+ if (err)
+ goto out;
+ priv->bittime = *bt;
+ if (bt->type == CAN_BITTIME_STD && bt->std.brp) {
+ priv->bitrate = priv->can_sys_clock /
+ (bt->std.brp * (1 + bt->std.prop_seg +
+ bt->std.phase_seg1 +
+ bt->std.phase_seg2));
+ } else
+ priv->bitrate = CAN_BITRATE_UNKNOWN;
+ }
+out:
+ return err;
+}
+
+static int can_get_custombittime(struct net_device *dev,
+ struct can_bittime *bt)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ *bt = priv->bittime;
+ return 0;
+}
+
+static int can_set_ctrlmode(struct net_device *dev, u32 ctrlmode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!priv->do_set_ctrlmode)
+ return -ENOTSUPP;
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ return priv->do_set_ctrlmode(dev, ctrlmode);
+}
+
+static int can_get_ctrlmode(struct net_device *dev, u32 *ctrlmode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ *ctrlmode = priv->ctrlmode;
+ return 0;
+}
+
+static int can_get_state(struct net_device *dev, can_state_t *state)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->do_get_state) {
+ int err = priv->do_get_state(dev, state);
+ if (err)
+ return err;
+ priv->state = *state;
+ } else
+ *state = priv->state;
+ return 0;
+}
+
+static int can_set_clock(struct net_device *dev, u32 clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ priv->can_sys_clock = clock;
+ return 0;
+}
+
+static int can_get_clock(struct net_device *dev, u32 *clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ *clock = priv->can_sys_clock;
+ return 0;
+}
+
+static int can_set_restart_ms(struct net_device *dev, int ms)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->restart_ms < 0)
+ return -EOPNOTSUPP;
+ priv->restart_ms = ms;
+ return 0;
+}
+
+static int can_get_restart_ms(struct net_device *dev, int *ms)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ *ms = priv->restart_ms;
+ return 0;
+}
+
+static int can_set_echo(struct net_device *dev, int on)
+{
+ if (on)
+ dev->flags |= IFF_ECHO;
+ else
+ dev->flags &= ~IFF_ECHO;
+ return 0;
+}
+
+static int can_get_echo(struct net_device *dev, int *on)
+{
+ *on = dev->flags & IFF_ECHO ? 1 : 0;
+ return 0;
+}
+
+/*
+ * SYSFS access functions and attributes.
+ * Use same locking as net/core/net-sysfs.c
+ */
+static inline int dev_isalive(const struct net_device *dev)
+{
+ return dev->reg_state <= NETREG_REGISTERED;
+}
+
+#define CAN_ATTR(_name, _func, _type, _fmt) \
+static ssize_t can_show_##_func(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct net_device *ndev = to_net_dev(dev); \
+ _type val; \
+ int ret = -EINVAL; \
+ read_lock(&dev_base_lock); \
+ if (dev_isalive(ndev)) { \
+ can_get_##_func(ndev, &val); \
+ ret = snprintf(buf, PAGE_SIZE, _fmt "\n", val); \
+ } \
+ read_unlock(&dev_base_lock); \
+ return ret; \
+} \
+static ssize_t can_store_##_func(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct net_device *ndev = to_net_dev(dev); \
+ char *endp; \
+ _type val; \
+ int ret = -EINVAL; \
+ val = simple_strtoul(buf, &endp, 0); \
+ if (endp == buf) \
+ return ret; \
+ rtnl_lock(); \
+ if (dev_isalive(ndev)) { \
+ if ((ret = can_set_##_func(ndev, val)) == 0) \
+ ret = count; \
+ } \
+ rtnl_unlock(); \
+ return ret; \
+} \
+static DEVICE_ATTR(_name, S_IRUGO | S_IWUSR, \
+ can_show_##_func, can_store_##_func)
+
+CAN_ATTR(can_bitrate, bitrate, u32, "%d");
+CAN_ATTR(can_restart_ms, restart_ms, int, "%d");
+CAN_ATTR(can_clock, clock, u32, "%d");
+CAN_ATTR(can_echo, echo, int, "%d");
+
+#define CAN_STATS_ATTR(_name) \
+static ssize_t can_stats_show_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct net_device *ndev = to_net_dev(dev); \
+ struct can_priv *priv = netdev_priv(ndev); \
+ int ret = -EINVAL; \
+ read_lock(&dev_base_lock); \
+ if (dev_isalive(ndev)) { \
+ ret= snprintf(buf, PAGE_SIZE, "%d\n", \
+ priv->can_stats._name); \
+ } \
+ read_unlock(&dev_base_lock); \
+ return ret; \
+} \
+static DEVICE_ATTR(_name, S_IRUGO, can_stats_show_##_name, NULL)
+
+#define CAN_CREATE_FILE(_dev, _name) \
+ if (device_create_file(&_dev->dev, &dev_attr_##_name)) \
+ dev_err(ND2D(_dev), \
+ "Couldn't create device file for ##_name\n")
+
+#define CAN_REMOVE_FILE(_dev, _name) \
+ device_remove_file(&_dev->dev, &dev_attr_##_name) \
+
+CAN_STATS_ATTR(error_warning);
+CAN_STATS_ATTR(error_passive);
+CAN_STATS_ATTR(bus_error);
+CAN_STATS_ATTR(arbitration_lost);
+CAN_STATS_ATTR(data_overrun);
+CAN_STATS_ATTR(wakeup);
+CAN_STATS_ATTR(restarts);
+
+static ssize_t can_store_restart(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ int ret = -EINVAL;
+
+ rtnl_lock();
+ if (dev_isalive(ndev)) {
+ if (!(ret = can_restart_now(ndev)))
+ ret = count;
+ }
+ rtnl_unlock();
+ return ret;
+}
+
+static DEVICE_ATTR(can_restart, S_IWUSR, NULL, can_store_restart);
+
+static const char *can_state_names[] = {
+ "active", "bus-warn", "bus-pass" , "bus-off",
+ "stopped", "sleeping", "unkown"
+};
+
+static ssize_t can_show_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ can_state_t state;
+ int ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(ndev)) {
+ can_get_state(ndev, &state);
+
+ if (state >= ARRAY_SIZE(can_state_names))
+ state = ARRAY_SIZE(can_state_names) - 1;
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", can_state_names[state]);
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static DEVICE_ATTR(can_state, S_IRUGO, can_show_state, NULL);
+
+static ssize_t can_show_ctrlmode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ u32 ctrlmode;
+ int ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(ndev)) {
+ can_get_ctrlmode(ndev, &ctrlmode);
+ ret = 0;
+ if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ ret += sprintf(buf + ret, "listenonly");
+ if (ret)
+ ret += sprintf(buf + ret, " ");
+ if (ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ ret += sprintf(buf + ret, "loopback");
+ if (ret)
+ ret += sprintf(buf + ret, "\n");
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static ssize_t can_store_ctrlmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ u32 ctrlmode = 0;
+ int ret = -EINVAL;
+
+ if (strstr(buf, "listenonly"))
+ ctrlmode |= CAN_CTRLMODE_LISTENONLY;
+ if (strstr(buf, "loopback"))
+ ctrlmode |= CAN_CTRLMODE_LOOPBACK;
+
+ rtnl_lock();
+ if (dev_isalive(ndev) && count) {
+ if (!(ret = can_set_ctrlmode(ndev, ctrlmode)))
+ ret = count;
+ }
+ rtnl_unlock();
+ return ret;
+}
+
+static DEVICE_ATTR(can_ctrlmode, S_IRUGO | S_IWUSR,
+ can_show_ctrlmode, can_store_ctrlmode);
+
+static ssize_t can_show_custombittime(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct can_bittime bt;
+ int ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(ndev)) {
+ can_get_custombittime(ndev, &bt);
+
+ if (bt.type == CAN_BITTIME_STD)
+ ret = snprintf(buf, PAGE_SIZE,
+ "std %#x %#x %#x %#x %#x %#x\n",
+ bt.std.brp, bt.std.prop_seg,
+ bt.std.phase_seg1, bt.std.phase_seg2,
+ bt.std.sjw, bt.std.sam);
+ else if (bt.type == CAN_BITTIME_BTR)
+ ret = snprintf(buf, PAGE_SIZE,
+ "btr %#x %#x\n",
+ bt.btr.btr0, bt.btr.btr1);
+ else
+ ret = snprintf(buf, PAGE_SIZE, "undefined\n");
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static ssize_t can_store_custombittime(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct can_bittime bt;
+ u32 val[6];
+ int ret = -EINVAL;
+
+ if (!strncmp(buf, "std", 3)) {
+ if (sscanf(buf + 4, "%i %i %i %i %i %i",
+ val, val + 1, val + 2, val + 3,
+ val + 4, val + 5) == 6) {
+ bt.type = CAN_BITTIME_STD;
+ bt.std.brp = val[0];
+ bt.std.prop_seg = val[1];
+ bt.std.phase_seg1 = val[2];
+ bt.std.phase_seg2 = val[3];
+ bt.std.sjw = val[4];
+ bt.std.sam = val[5];
+ }
+ }
+ else if (!strncmp(buf, "btr", 3)) {
+ if (sscanf(buf + 4, "%i %i", val, val + 1) == 2) {
+ bt.type = CAN_BITTIME_BTR;
+ bt.btr.btr0 = val[0];
+ bt.btr.btr1 = val[1];
+ }
+ } else
+ goto out;
+
+ rtnl_lock();
+ if (dev_isalive(ndev)) {
+ if (!(ret = can_set_custombittime(ndev, &bt)))
+ ret = count;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+static DEVICE_ATTR(can_custombittime, S_IRUGO | S_IWUSR,
+ can_show_custombittime, can_store_custombittime);
+
+static struct attribute *can_stats_attrs[] = {
+ &dev_attr_error_warning.attr,
+ &dev_attr_error_passive.attr,
+ &dev_attr_bus_error.attr,
+ &dev_attr_arbitration_lost.attr,
+ &dev_attr_data_overrun.attr,
+ &dev_attr_wakeup.attr,
+ &dev_attr_restarts.attr,
+ NULL
+};
+
+static struct attribute_group can_stats_group = {
+ .name = "can_statistics",
+ .attrs = can_stats_attrs,
+};
+
+void can_create_sysfs(struct net_device *dev)
+{
+ int err;
+
+ CAN_CREATE_FILE(dev, can_bitrate);
+ CAN_CREATE_FILE(dev, can_custombittime);
+ CAN_CREATE_FILE(dev, can_restart);
+ CAN_CREATE_FILE(dev, can_ctrlmode);
+ CAN_CREATE_FILE(dev, can_state);
+ CAN_CREATE_FILE(dev, can_restart_ms);
+ CAN_CREATE_FILE(dev, can_clock);
+ CAN_CREATE_FILE(dev, can_echo);
+
+ err = sysfs_create_group(&(dev->dev.kobj), &can_stats_group);
+ if (err) {
+ printk(KERN_EMERG
+ "couldn't create sysfs group for CAN stats\n");
+ }
+}
+
+void can_remove_sysfs(struct net_device *dev)
+{
+ CAN_REMOVE_FILE(dev, can_bitrate);
+ CAN_REMOVE_FILE(dev, can_custombittime);
+ CAN_REMOVE_FILE(dev, can_restart);
+ CAN_REMOVE_FILE(dev, can_ctrlmode);
+ CAN_REMOVE_FILE(dev, can_state);
+ CAN_REMOVE_FILE(dev, can_clock);
+ CAN_REMOVE_FILE(dev, can_echo);
+
+ sysfs_remove_group(&(dev->dev.kobj), &can_stats_group);
+}
+
+#endif /* CONFIG_SYSFS */
+
+
+
--- /dev/null
+/*
+ * $Id: dev.c 542 2007-11-07 13:57:16Z thuermann $
+ *
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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
+ */
+
+#ifndef CAN_SYSFS_H
+#define CAN_SYSFS_H
+
+void can_create_sysfs(struct net_device *dev);
+void can_remove_sysfs(struct net_device *dev);
+
+#endif /* CAN_SYSFS_H */