diff options
Diffstat (limited to 'drivers/net/can/sja1000')
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 11 | ||||
-rw-r--r-- | drivers/net/can/sja1000/ems_pci.c | 320 | ||||
-rw-r--r-- | drivers/net/can/sja1000/kvaser_pci.c | 412 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000.c | 637 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000.h | 181 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000_of_platform.c | 235 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000_platform.c | 165 |
7 files changed, 1961 insertions, 0 deletions
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile new file mode 100644 index 00000000000..9d0c08da273 --- /dev/null +++ b/drivers/net/can/sja1000/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the SJA1000 CAN controller drivers. +# + +obj-$(CONFIG_CAN_SJA1000) += sja1000.o +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 + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c new file mode 100644 index 00000000000..121b64101d7 --- /dev/null +++ b/drivers/net/can/sja1000/ems_pci.c @@ -0,0 +1,320 @@ +/* + * 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 <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "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(card->base_addr + (port * EMS_PCI_PORT_BYTES)); +} + +static u8 ems_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + (port * EMS_PCI_PORT_BYTES)); +} + +static void ems_pci_write_reg(const struct sja1000_priv *priv, int port, u8 val) +{ + writeb(val, priv->reg_base + (port * EMS_PCI_PORT_BYTES)); +} + +static void ems_pci_post_irq(const struct sja1000_priv *priv) +{ + 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(const struct sja1000_priv *priv) +{ + unsigned char res; + + /* Make sure SJA1000 is in reset mode */ + ems_pci_write_reg(priv, REG_MOD, 1); + + ems_pci_write_reg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + res = ems_pci_read_reg(priv, 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; + priv->irq_flags = IRQF_SHARED; + + dev->irq = pdev->irq; + priv->reg_base = card->base_addr + EMS_PCI_CAN_BASE_OFFSET + + (i * EMS_PCI_CAN_CTRL_SIZE); + + /* Check if channel is present */ + if (ems_pci_check_chan(priv)) { + priv->read_reg = ems_pci_read_reg; + priv->write_reg = ems_pci_write_reg; + priv->post_irq = ems_pci_post_irq; + priv->can.clock.freq = 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 0x%p, irq %d\n", + i + 1, priv->reg_base, 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); + diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c new file mode 100644 index 00000000000..7dd7769b971 --- /dev/null +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -0,0 +1,412 @@ +/* + * 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 <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "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(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +static void kvaser_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + iowrite8(val, priv->reg_base + 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 intcsr; + + /* Disable interrupts from card */ + intcsr = ioread32(board->conf_addr + S5920_INTCSR); + intcsr &= ~INTCSR_ADDON_INTENABLE_M; + iowrite32(intcsr, 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_en_io; + + /* Enable interrupts from card */ + tmp_en_io = ioread32(board->conf_addr + S5920_INTCSR); + tmp_en_io |= INTCSR_ADDON_INTENABLE_M; + iowrite32(tmp_en_io, 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); + /* 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); + board = priv->priv; + if (!board) + return; + + dev_info(&board->pci_dev->dev, "Removing device %s\n", + dev->name); + + /* Disable PCI interrupts */ + kvaser_pci_disable_irq(dev); + + 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); + + pci_iounmap(board->pci_dev, priv->reg_base); + 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, + void __iomem *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); + + /* 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; + } + + priv->reg_base = base_addr + channel * KVASER_PCI_PORT_BYTES; + + priv->read_reg = kvaser_pci_read_reg; + priv->write_reg = kvaser_pci_write_reg; + + priv->can.clock.freq = KVASER_PCI_CAN_CLOCK; + + priv->ocr = KVASER_PCI_OCR; + priv->cdr = KVASER_PCI_CDR; + + priv->irq_flags = IRQF_SHARED; + dev->irq = pdev->irq; + + init_step = 4; + + dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n", + priv->reg_base, 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 == NULL) { + err = -ENODEV; + goto failure_release_regions; + } + + /* XILINX board wide address */ + res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE); + if (res_addr == NULL) { + err = -ENOMEM; + goto failure_iounmap; + } + + base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE); + if (base_addr == NULL) { + 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, + 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 != NULL) + pci_iounmap(pdev, conf_addr); + if (res_addr != NULL) + pci_iounmap(pdev, res_addr); + if (base_addr != NULL) + pci_iounmap(pdev, base_addr); + +failure_release_regions: + 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); diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c new file mode 100644 index 00000000000..571f133a8fe --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.c @@ -0,0 +1,637 @@ +/* + * 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/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 "sja1000.h" + +#define DRV_NAME "sja1000" + +MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver"); + +static struct can_bittiming_const sja1000_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int sja1000_probe_chip(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + if (priv->reg_base && (priv->read_reg(priv, 0) == 0xFF)) { + printk(KERN_INFO "%s: probing @0x%lX failed\n", + DRV_NAME, dev->base_addr); + return 0; + } + return -1; +} + +static void set_reset_mode(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(priv, REG_MOD); + int i; + + /* disable interrupts */ + priv->write_reg(priv, REG_IER, IRQ_OFF); + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if (status & MOD_RM) { + priv->can.state = CAN_STATE_STOPPED; + return; + } + + priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */ + udelay(10); + status = priv->read_reg(priv, REG_MOD); + } + + dev_err(dev->dev.parent, "setting SJA1000 into reset mode failed!\n"); +} + +static void set_normal_mode(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(priv, REG_MOD); + int i; + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if ((status & MOD_RM) == 0) { + priv->can.state = CAN_STATE_ERROR_ACTIVE; + /* enable all interrupts */ + priv->write_reg(priv, REG_IER, IRQ_ALL); + return; + } + + /* set chip to normal mode */ + priv->write_reg(priv, REG_MOD, 0x00); + udelay(10); + status = priv->read_reg(priv, REG_MOD); + } + + dev_err(dev->dev.parent, "setting SJA1000 into normal mode failed!\n"); +} + +static void sja1000_start(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + /* leave reset mode */ + if (priv->can.state != CAN_STATE_STOPPED) + set_reset_mode(dev); + + /* Clear error counters and error code capture */ + priv->write_reg(priv, REG_TXERR, 0x0); + priv->write_reg(priv, REG_RXERR, 0x0); + priv->read_reg(priv, REG_ECC); + + /* leave reset mode */ + set_normal_mode(dev); +} + +static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + sja1000_start(dev); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int sja1000_set_bittiming(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + dev_info(dev->dev.parent, + "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + priv->write_reg(priv, REG_BTR0, btr0); + priv->write_reg(priv, 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(priv, REG_CDR, priv->cdr | CDR_PELICAN); + + /* set acceptance filter (accept all) */ + priv->write_reg(priv, REG_ACCC0, 0x00); + priv->write_reg(priv, REG_ACCC1, 0x00); + priv->write_reg(priv, REG_ACCC2, 0x00); + priv->write_reg(priv, REG_ACCC3, 0x00); + + priv->write_reg(priv, REG_ACCM0, 0xFF); + priv->write_reg(priv, REG_ACCM1, 0xFF); + priv->write_reg(priv, REG_ACCM2, 0xFF); + priv->write_reg(priv, REG_ACCM3, 0xFF); + + priv->write_reg(priv, 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->stats; + 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(priv, REG_FI, fi); + priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); + priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8)); + priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5); + priv->write_reg(priv, REG_ID4, (id & 0x0000001f) << 3); + } else { + dreg = SFF_BUF; + priv->write_reg(priv, REG_FI, fi); + priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3); + priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5); + } + + for (i = 0; i < dlc; i++) + priv->write_reg(priv, dreg++, cf->data[i]); + + stats->tx_bytes += dlc; + dev->trans_start = jiffies; + + can_put_echo_skb(skb, dev, 0); + + priv->write_reg(priv, 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->stats; + 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(priv, REG_FI); + dlc = fi & 0x0F; + + if (fi & FI_FF) { + /* extended frame format (EFF) */ + dreg = EFF_BUF; + id = (priv->read_reg(priv, REG_ID1) << (5 + 16)) + | (priv->read_reg(priv, REG_ID2) << (5 + 8)) + | (priv->read_reg(priv, REG_ID3) << 5) + | (priv->read_reg(priv, REG_ID4) >> 3); + id |= CAN_EFF_FLAG; + } else { + /* standard frame format (SFF) */ + dreg = SFF_BUF; + id = (priv->read_reg(priv, REG_ID1) << 3) + | (priv->read_reg(priv, 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(priv, dreg++); + + while (i < 8) + cf->data[i++] = 0; + + /* release receive buffer */ + priv->write_reg(priv, 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) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state 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 */ + dev_dbg(dev->dev.parent, "data overrun interrupt\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + priv->write_reg(priv, REG_CMR, CMD_CDO); /* clear bit */ + } + + if (isrc & IRQ_EI) { + /* error warning interrupt */ + dev_dbg(dev->dev.parent, "error warning interrupt\n"); + + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } else if (status & SR_ES) { + state = CAN_STATE_ERROR_WARNING; + } else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & IRQ_BEI) { + /* bus error interrupt */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + ecc = priv->read_reg(priv, REG_ECC); + + 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 */ + dev_dbg(dev->dev.parent, "error passive interrupt\n"); + if (status & SR_ES) + state = CAN_STATE_ERROR_PASSIVE; + else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & IRQ_ALI) { + /* arbitration lost interrupt */ + dev_dbg(dev->dev.parent, "arbitration lost interrupt\n"); + alc = priv->read_reg(priv, REG_ALC); + priv->can.can_stats.arbitration_lost++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = alc & 0x1f; + } + + if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + uint8_t rxerr = priv->read_reg(priv, REG_RXERR); + uint8_t txerr = priv->read_reg(priv, REG_TXERR); + cf->can_id |= CAN_ERR_CRTL; + if (state == CAN_STATE_ERROR_WARNING) { + priv->can.can_stats.error_warning++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } else { + priv->can.can_stats.error_passive++; + 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; +} + +irqreturn_t sja1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + uint8_t isrc, status; + int n = 0; + + /* Shared interrupts and IRQ off? */ + if (priv->read_reg(priv, REG_IER) == IRQ_OFF) + return IRQ_NONE; + + if (priv->pre_irq) + priv->pre_irq(priv); + + while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) { + n++; + status = priv->read_reg(priv, REG_SR); + + if (isrc & IRQ_WUI) + dev_warn(dev->dev.parent, "wakeup interrupt\n"); + + 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(priv, REG_SR); + } + } + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { + /* error interrupt */ + if (sja1000_err(dev, isrc, status)) + break; + } + } + + if (priv->post_irq) + priv->post_irq(priv); + + if (n >= SJA1000_MAX_IRQ) + dev_dbg(dev->dev.parent, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} +EXPORT_SYMBOL_GPL(sja1000_interrupt); + +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); + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + /* register interrupt handler, if not done by the device driver */ + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) { + err = request_irq(dev->irq, &sja1000_interrupt, priv->irq_flags, + dev->name, (void *)dev); + if (err) { + close_candev(dev); + return -EAGAIN; + } + } + + /* 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); + + netif_stop_queue(dev); + set_reset_mode(dev); + + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) + free_irq(dev->irq, (void *)dev); + + close_candev(dev); + + priv->open_time = 0; + + 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; + priv->can.bittiming_const = &sja1000_bittiming_const; + priv->can.do_set_bittiming = sja1000_set_bittiming; + priv->can.do_set_mode = sja1000_set_mode; + + 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_GPL(free_sja1000dev); + +static const struct net_device_ops sja1000_netdev_ops = { + .ndo_open = sja1000_open, + .ndo_stop = sja1000_close, + .ndo_start_xmit = sja1000_start_xmit, +}; + +int register_sja1000dev(struct net_device *dev) +{ + if (!sja1000_probe_chip(dev)) + return -ENODEV; + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &sja1000_netdev_ops; + + set_reset_mode(dev); + chipset_init(dev); + + return register_candev(dev); +} +EXPORT_SYMBOL_GPL(register_sja1000dev); + +void unregister_sja1000dev(struct net_device *dev) +{ + set_reset_mode(dev); + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_sja1000dev); + +static __init int sja1000_init(void) +{ + printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME); + + return 0; +} + +module_init(sja1000_init); + +static __exit void sja1000_exit(void) +{ + printk(KERN_INFO "%s: driver removed\n", DRV_NAME); +} + +module_exit(sja1000_exit); diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h new file mode 100644 index 00000000000..302d2c763ad --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.h @@ -0,0 +1,181 @@ +/* + * 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 SJA1000_DEV_H +#define SJA1000_DEV_H + +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> + +#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ + +/* 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 + +/* + * Flags for sja1000priv.flags + */ +#define SJA1000_CUSTOM_IRQ_HANDLER 0x1 + +/* + * SJA1000 private data structure + */ +struct sja1000_priv { + struct can_priv can; /* must be the first member */ + int open_time; + struct sk_buff *echo_skb; + + /* the lower-layer is responsible for appropriate locking */ + u8 (*read_reg) (const struct sja1000_priv *priv, int reg); + void (*write_reg) (const struct sja1000_priv *priv, int reg, u8 val); + void (*pre_irq) (const struct sja1000_priv *priv); + void (*post_irq) (const struct sja1000_priv *priv); + + void *priv; /* for board-specific data */ + struct net_device *dev; + + void __iomem *reg_base; /* ioremap'ed address to registers */ + unsigned long irq_flags; /* for request_irq() */ + + u16 flags; /* custom mode flags */ + u8 ocr; /* output control register */ + u8 cdr; /* clock divider register */ +}; + +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); + +irqreturn_t sja1000_interrupt(int irq, void *dev_id); + +#endif /* SJA1000_DEV_H */ diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c new file mode 100644 index 00000000000..3373560405b --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -0,0 +1,235 @@ +/* + * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus + * + * Copyright (C) 2008-2009 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. + */ + +/* This is a generic driver for SJA1000 chips on the OpenFirmware platform + * bus found on embedded PowerPC systems. You need a SJA1000 CAN node + * definition in your flattened device tree source (DTS) file similar to: + * + * can@3,100 { + * compatible = "nxp,sja1000"; + * reg = <3 0x100 0x80>; + * interrupts = <2 0>; + * interrupt-parent = <&mpic>; + * nxp,external-clock-frequency = <16000000>; + * }; + * + * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further + * information. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include <linux/of_platform.h> +#include <asm/prom.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_of_platform" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); +MODULE_LICENSE("GPL v2"); + +#define SJA1000_OFP_CAN_CLOCK (16000000 / 2) + +#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN +#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) + +static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) +{ + return in_8(priv->reg_base + reg); +} + +static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, + int reg, u8 val) +{ + out_8(priv->reg_base + reg, val); +} + +static int __devexit sja1000_ofp_remove(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct sja1000_priv *priv = netdev_priv(dev); + struct device_node *np = ofdev->node; + struct resource res; + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + iounmap(priv->reg_base); + irq_dispose_mapping(dev->irq); + + of_address_to_resource(np, 0, &res); + release_mem_region(res.start, resource_size(&res)); + + return 0; +} + +static int __devinit sja1000_ofp_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct device_node *np = ofdev->node; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource res; + const u32 *prop; + int err, irq, res_size, prop_size; + void __iomem *base; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_err(&ofdev->dev, "invalid address\n"); + return err; + } + + res_size = resource_size(&res); + + if (!request_mem_region(res.start, res_size, DRV_NAME)) { + dev_err(&ofdev->dev, "couldn't request %#llx..%#llx\n", + (unsigned long long)res.start, + (unsigned long long)res.end); + return -EBUSY; + } + + base = ioremap_nocache(res.start, res_size); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap %#llx..%#llx\n", + (unsigned long long)res.start, + (unsigned long long)res.end); + err = -ENOMEM; + goto exit_release_mem; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_dispose_irq; + } + + priv = netdev_priv(dev); + + priv->read_reg = sja1000_ofp_read_reg; + priv->write_reg = sja1000_ofp_write_reg; + + prop = of_get_property(np, "nxp,external-clock-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->can.clock.freq = *prop / 2; + else + priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ + + prop = of_get_property(np, "nxp,tx-output-mode", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->ocr |= *prop & OCR_MODE_MASK; + else + priv->ocr |= OCR_MODE_NORMAL; /* default */ + + prop = of_get_property(np, "nxp,tx-output-config", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK; + else + priv->ocr |= OCR_TX0_PULLDOWN; /* default */ + + prop = of_get_property(np, "nxp,clock-out-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32)) && *prop) { + u32 divider = priv->can.clock.freq * 2 / *prop; + + if (divider > 1) + priv->cdr |= divider / 2 - 1; + else + priv->cdr |= CDR_CLKOUT_MASK; + } else { + priv->cdr |= CDR_CLK_OFF; /* default */ + } + + prop = of_get_property(np, "nxp,no-comparator-bypass", NULL); + if (!prop) + priv->cdr |= CDR_CBP; /* default */ + + priv->irq_flags = IRQF_SHARED; + priv->reg_base = base; + + dev->irq = irq; + + dev_info(&ofdev->dev, + "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", + priv->reg_base, dev->irq, priv->can.clock.freq, + priv->ocr, priv->cdr); + + dev_set_drvdata(&ofdev->dev, dev); + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_sja1000; + } + + return 0; + +exit_free_sja1000: + free_sja1000dev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + release_mem_region(res.start, res_size); + + return err; +} + +static struct of_device_id __devinitdata sja1000_ofp_table[] = { + {.compatible = "nxp,sja1000"}, + {}, +}; + +static struct of_platform_driver sja1000_ofp_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .probe = sja1000_ofp_probe, + .remove = __devexit_p(sja1000_ofp_remove), + .match_table = sja1000_ofp_table, +}; + +static int __init sja1000_ofp_init(void) +{ + return of_register_platform_driver(&sja1000_ofp_driver); +} +module_init(sja1000_ofp_init); + +static void __exit sja1000_ofp_exit(void) +{ + return of_unregister_platform_driver(&sja1000_ofp_driver); +}; +module_exit(sja1000_ofp_exit); diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c new file mode 100644 index 00000000000..628374c2a05 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005 Sascha Hauer, Pengutronix + * 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/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/irq.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_platform" + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_LICENSE("GPL v2"); + +static u8 sp_read_reg(const struct sja1000_priv *priv, int reg) +{ + return ioread8(priv->reg_base + reg); +} + +static void sp_write_reg(const struct sja1000_priv *priv, int reg, u8 val) +{ + iowrite8(val, priv->reg_base + reg); +} + +static int sp_probe(struct platform_device *pdev) +{ + int err; + void __iomem *addr; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res_mem, *res_irq; + struct sja1000_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -ENODEV; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_mem || !res_irq) { + err = -ENODEV; + goto exit; + } + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + DRV_NAME)) { + err = -EBUSY; + goto exit; + } + + addr = ioremap_nocache(res_mem->start, resource_size(res_mem)); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + priv = netdev_priv(dev); + + dev->irq = res_irq->start; + priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK; + priv->reg_base = addr; + priv->read_reg = sp_read_reg; + priv->write_reg = sp_write_reg; + priv->can.clock.freq = pdata->clock; + priv->ocr = pdata->ocr; + priv->cdr = pdata->cdr; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + + dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n", + DRV_NAME, priv->reg_base, dev->irq); + return 0; + + exit_free: + free_sja1000dev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res_mem->start, resource_size(res_mem)); + exit: + return err; +} + +static int sp_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct sja1000_priv *priv = netdev_priv(dev); + struct resource *res; + + unregister_sja1000dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + + if (priv->reg_base) + iounmap(priv->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sp_driver = { + .probe = sp_probe, + .remove = sp_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sp_init(void) +{ + return platform_driver_register(&sp_driver); +} + +static void __exit sp_exit(void) +{ + platform_driver_unregister(&sp_driver); +} + +module_init(sp_init); +module_exit(sp_exit); |