* [PATCH 0/2] can/sja1000: Add/upgrade PEAK-System sja1000 driver @ 2012-01-31 13:19 Stephane Grosjean 2012-01-31 13:19 ` [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean 2012-01-31 13:19 ` [PATCH 2/2] can/sja1000: Add support of PEAK-System PCMCIA board Stephane Grosjean 0 siblings, 2 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-01-31 13:19 UTC (permalink / raw) To: Oliver Hartkopp; +Cc: linux-can Mailing List, Stephane Grosjean That serie of patches upgrades the peak_pci driver and adds the peak_pcmcia new driver, to handle all the sja1000 PCI based from PEAK-System Technik (http://www.peak-system.com). These patches are planned for kernel v3.02.00 and above. Stephane Grosjean (1): can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean (1): can/sja1000: Add support of PEAK-System PCMCIA board drivers/net/can/sja1000/Kconfig | 9 + drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/peak_pci.c | 521 ++++++++++++++++++++++-- drivers/net/can/sja1000/peak_pcmcia.c | 721 +++++++++++++++++++++++++++++++++ 4 files changed, 1212 insertions(+), 40 deletions(-) create mode 100644 drivers/net/can/sja1000/peak_pcmcia.c -- 1.7.5.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 13:19 [PATCH 0/2] can/sja1000: Add/upgrade PEAK-System sja1000 driver Stephane Grosjean @ 2012-01-31 13:19 ` Stephane Grosjean 2012-01-31 13:25 ` Marc Kleine-Budde 2012-01-31 22:54 ` Marc Kleine-Budde 2012-01-31 13:19 ` [PATCH 2/2] can/sja1000: Add support of PEAK-System PCMCIA board Stephane Grosjean 1 sibling, 2 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-01-31 13:19 UTC (permalink / raw) To: Oliver Hartkopp; +Cc: linux-can Mailing List, Stephane Grosjean This patch adds the support of some new PEAK-System Technik sja1000 boards (http://www.peak-system.com): PCAN-PCI Express (1 or 2 channels) PCAN-ExpressCard (1 or 2 channels) PCAN-miniPCI (1 or 2 channels) All of these boards are compliant with CAN specifications 2.0A (11-bit ID) and 2.0B (29-bit ID). This patch also fixes the management of the channels list. Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com> --- drivers/net/can/sja1000/peak_pci.c | 521 +++++++++++++++++++++++++++++++++--- 1 files changed, 481 insertions(+), 40 deletions(-) diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 2c7f503..02d94bd 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -1,9 +1,9 @@ /* * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> * - * Derived from the PCAN project file driver/src/pcan_pci.c: + * Derived from the PCAN project files driver/src/pcan_pci.c: * - * Copyright (C) 2001-2006 PEAK System-Technik GmbH + * Copyright (C) 2001-2012 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 @@ -13,12 +13,7 @@ * 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> @@ -26,46 +21,117 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/io.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> #include <linux/can.h> #include <linux/can/dev.h> - #include "sja1000.h" MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); -MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI/PCIe cards"); -MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe CAN card"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe/PCIeC miniPCI CAN cards"); MODULE_LICENSE("GPL v2"); -#define DRV_NAME "peak_pci" - -struct peak_pci_chan { - void __iomem *cfg_base; /* Common for all channels */ - struct net_device *next_dev; /* Chain of network devices */ - u16 icr_mask; /* Interrupt mask for fast ack */ -}; +#define PEAK_PCI_DRV_NAME "peak_pci" +#define PEAK_PCI_CHAN_MAX 4 #define PEAK_PCI_CAN_CLOCK (16000000 / 2) #define PEAK_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) #define PEAK_PCI_OCR OCR_TX0_PUSHPULL -/* - * Important PITA registers - */ +/* PITA registers */ #define PITA_ICR 0x00 /* Interrupt control register */ #define PITA_GPIOICR 0x18 /* GPIO interface control register */ #define PITA_MISC 0x1C /* Miscellaneous register */ +/* GPIOICR byte access offsets */ +#define PITA_GPOUT 0x18 /* GPx output value */ +#define PITA_GPIN 0x19 /* GPx input value */ +#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */ + +/* I2C GP bits */ +#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */ +#define PITA_GPIN_SDA 0x04 /* Serial DAta line */ + +/* LEDs control */ +#define PCA9553_1_SLAVEADDR (0xC4 >> 1) + +/* PCA9553 LS0 fields values */ +enum { + PCA9553_LOW, + PCA9553_HIGHZ, + PCA9553_PWM0, + PCA9553_PWM1 +}; + +#define PCA9553_ON PCA9553_LOW +#define PCA9553_OFF PCA9553_HIGHZ +#define PCA9553_SLOW PCA9553_PWM0 +#define PCA9553_FAST PCA9553_PWM1 + +#define PCA9553_LED(c) (1 << (c)) +#define PCA9553_LED_STATE(s, c) ((s) << ((c) << 1)) + +#define PCA9553_LED_ON(c) PCA9553_LED_STATE(PCA9553_ON, c) +#define PCA9553_LED_OFF(c) PCA9553_LED_STATE(PCA9553_OFF, c) +#define PCA9553_LED_SLOW(c) PCA9553_LED_STATE(PCA9553_SLOW, c) +#define PCA9553_LED_FAST(c) PCA9553_LED_STATE(PCA9553_FAST, c) +#define PCA9553_LED_MASK(c) PCA9553_LED_STATE(0x03, c) + +#define PCA9553_LED_OFF_ALL (PCA9553_LED_OFF(0) | PCA9553_LED_OFF(1)) + +#define PCA9553_LS0_INIT 0x40 /* initial value (!= from 0x00) */ + +#define PCA9553_LS0_NONE 0x45 /* off value */ + #define PEAK_PCI_CFG_SIZE 0x1000 /* Size of the config PCI bar */ #define PEAK_PCI_CHAN_SIZE 0x0400 /* Size used by the channel */ +/* PCI stuff */ #define PEAK_PCI_VENDOR_ID 0x001C /* The PCI device and vendor IDs */ #define PEAK_PCI_DEVICE_ID 0x0001 /* for PCI/PCIe slot cards */ +#define PEAK_PCIEC_DEVICE_ID 0x0002 /* for ExpressCard slot cards */ +#define PEAK_PCIE_DEVICE_ID 0x0003 /* for nextgen PCIe slot cards */ +#define PEAK_MPCI_DEVICE_ID 0x0008 /* The miniPCI slot cards */ + +/* PCAN-PCIeC specific (leds management) */ +struct peak_pciec_chan { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; -static const u16 peak_pci_icr_masks[] = {0x02, 0x01, 0x40, 0x80}; +struct peak_pciec_card { + void __iomem *cfg_base; /* Common for all channels */ + void __iomem *reg_base; /* first channel base address */ + u8 led_cache; /* leds state cache */ + + /* PCIExpressCard i2c data */ + struct i2c_algo_bit_data i2c_bit; + struct i2c_adapter led_chip; + + struct timer_list led_timer; /* activity timer */ + int chan_count; + struct peak_pciec_chan channel[PEAK_PCI_CHAN_MAX]; +}; + +struct peak_pci_chan { + void __iomem *cfg_base; /* Common for all channels */ + struct net_device *prev_dev; /* Chain of network devices */ + u16 icr_mask; /* Interrupt mask for fast ack */ + struct peak_pciec_card *pciec_card; /* only for PCIeC */ +}; + +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { + 0x02, 0x01, 0x40, 0x80 +}; static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = { {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_MPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, {0,} }; @@ -85,20 +151,370 @@ static void peak_pci_write_reg(const struct sja1000_priv *priv, static void peak_pci_post_irq(const struct sja1000_priv *priv) { struct peak_pci_chan *chan = priv->priv; - u16 icr; + u16 icr = readw(chan->cfg_base + PITA_ICR); /* Select and clear in PITA stored interrupt */ - icr = readw(chan->cfg_base + PITA_ICR); if (icr & chan->icr_mask) writew(chan->icr_mask, chan->cfg_base + PITA_ICR); } +static inline void pita_set_scl_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SCL; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static inline void pita_set_sda_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SDA; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void peak_pciec_init_pita_gpio(struct peak_pciec_card *card) +{ + /* raise SCL & SDA GPIOs to high-Z */ + pita_set_scl_highz(card); + pita_set_sda_highz(card); +} + +static void pita_setsda(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output sda always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SDA; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output sda with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SDA; + else + gp_outen |= PITA_GPIN_SDA; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void pita_setscl(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output scl always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SCL; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output scl with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SCL; + else + gp_outen |= PITA_GPIN_SCL; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static int pita_getsda(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_sda_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SDA) ? 1 : 0; +} + +static int pita_getscl(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_scl_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SCL) ? 1 : 0; +} + +/* + * write commands to the LED chip though the I2C-bus of the PCAN-PCIeC + */ +static int peak_pciec_write_pca9553(struct peak_pciec_card *card, + u8 offset, u8 data) +{ + u8 buffer[2] = { + offset, + data + }; + struct i2c_msg msg = { + .addr = PCA9553_1_SLAVEADDR, + .len = 2, + .buf = buffer, + }; + int ret; + + /* cache led mask */ + if ((offset == 5) && (data == card->led_cache)) + return 0; + + ret = i2c_transfer(&card->led_chip, &msg, 1); + if (ret < 0) + return ret; + + if (offset == 5) + card->led_cache = data; + + return 0; +} + +/* + * timer callback used to control the LEDs + */ +static void peak_pciec_led_timer(unsigned long arg) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)arg; + struct net_device *netdev; + u8 new_led = card->led_cache; + int i, up_count = 0; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_ON(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_SLOW(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); + + /* restart timer (except if no more configured channels) */ + if (up_count) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * set LEDs blinking state + */ +static void peak_pciec_set_leds(struct peak_pciec_card *card, u8 led_mask, u8 s) +{ + u8 new_led = card->led_cache; + int i; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCA9553_LED(i)) { + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_STATE(s, i); + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); +} + +/* + * start one second timer to control LEDs + */ +static void peak_pciec_start_led_timer(struct peak_pciec_card *card) +{ + if (!timer_pending(&card->led_timer)) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * stop LEDs timer + */ +static void peak_pciec_stop_led_timer(struct peak_pciec_card *card) +{ + del_timer_sync(&card->led_timer); +} + +/* + * initialize the PCA9553 4-bit I2C-bus LED chip + */ +static int peak_pciec_init_leds(struct peak_pciec_card *card) +{ + int err; + + /* prescaler for frequency 0: "SLOW" = 1 Hz = "44" */ + err = peak_pciec_write_pca9553(card, 1, 44 / 1); + if (err) + return err; + + /* duty cycle 0: 50% */ + err = peak_pciec_write_pca9553(card, 2, 0x80); + if (err) + return err; + + /* prescaler for frequency 1: "FAST" = 5 Hz */ + err = peak_pciec_write_pca9553(card, 3, 44 / 5); + if (err) + return err; + + /* duty cycle 1: 50% */ + err = peak_pciec_write_pca9553(card, 4, 0x80); + if (err) + return err; + + /* switch LEDs to initial state */ + return peak_pciec_write_pca9553(card, 5, PCA9553_LS0_INIT); +} + +/* + * restore LEDs state to off peak_pciec_leds_exit + */ +static void peak_pciec_leds_exit(struct peak_pciec_card *card) +{ + /* switch LEDs to off */ + peak_pciec_write_pca9553(card, 5, PCA9553_LED_OFF_ALL); +} + +/* + * normal write sja1000 register method overloaded to catch when controller + * is started or stopped, to control leds + */ +static void peak_pciec_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card = chan->pciec_card; + int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (val) { + case MOD_RM: + /* Reset Mode: set led on */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_SLOW); + peak_pciec_start_led_timer(card); + break; + default: + break; + } + + /* call base function */ + peak_pci_write_reg(priv, port, val); +} + +static struct i2c_algo_bit_data peak_pciec_i2c_bit_ops = { + .setsda = pita_setsda, + .setscl = pita_setscl, + .getsda = pita_getsda, + .getscl = pita_getscl, + .udelay = 10, + .timeout = HZ, +}; + +static int peak_pciec_init(struct pci_dev *pdev, struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card; + int err; + + /* copy i2c object address from 1st channel */ + if (chan->prev_dev) { + struct sja1000_priv *prev_priv = netdev_priv(chan->prev_dev); + struct peak_pci_chan *prev_chan = prev_priv->priv; + + card = prev_chan->pciec_card; + if (!card) + return -ENODEV; + + /* channel is the first one: do the init part */ + } else { + /* create the bit banging I2C adapter structure */ + card = kzalloc(sizeof(struct peak_pciec_card), GFP_KERNEL); + if (!card) { + dev_warn(&pdev->dev, + "failed allocating memory for leds chip\n"); + return -ENOMEM; + } + + card->cfg_base = chan->cfg_base; + card->reg_base = priv->reg_base; + + card->led_chip.owner = THIS_MODULE; + card->led_chip.dev.parent = &pdev->dev; + card->led_chip.algo_data = &card->i2c_bit; + strncpy(card->led_chip.name, "peak_i2c", + sizeof(card->led_chip.name)); + + card->i2c_bit = peak_pciec_i2c_bit_ops; + card->i2c_bit.udelay = 10; + card->i2c_bit.timeout = HZ; + card->i2c_bit.data = card; + + peak_pciec_init_pita_gpio(card); + + err = i2c_bit_add_bus(&card->led_chip); + if (err) { + dev_warn(&pdev->dev, "i2c init failed\n"); + peak_pciec_init_pita_gpio(card); + kfree(card); + return err; + } + + err = peak_pciec_init_leds(card); + if (err) { + dev_warn(&pdev->dev, "leds hardware init failed\n"); + i2c_del_adapter(&card->led_chip); + peak_pciec_init_pita_gpio(card); + kfree(card); + return err; + } + + /* init the timer which controls the leds */ + init_timer(&card->led_timer); + card->led_timer.function = peak_pciec_led_timer; + card->led_timer.data = (unsigned long)card; + } + + chan->pciec_card = card; + card->channel[card->chan_count++].netdev = dev; + + return 0; +} + +static void peak_pciec_remove(struct peak_pciec_card *card) +{ + peak_pciec_stop_led_timer(card); + peak_pciec_leds_exit(card); + i2c_del_adapter(&card->led_chip); + peak_pciec_init_pita_gpio(card); + kfree(card); +} + static int __devinit peak_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct sja1000_priv *priv; struct peak_pci_chan *chan; - struct net_device *dev, *dev0 = NULL; + struct net_device *dev; void __iomem *cfg_base, *reg_base; u16 sub_sys_id, icr; int i, err, channels; @@ -107,7 +523,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, if (err) return err; - err = pci_request_regions(pdev, DRV_NAME); + err = pci_request_regions(pdev, PEAK_PCI_DRV_NAME); if (err) goto failure_disable_pci; @@ -168,8 +584,10 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, chan->cfg_base = cfg_base; priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE; + /* PCAN-ExpressCard needs some support for leds */ + priv->write_reg = (pdev->device == PEAK_PCIEC_DEVICE_ID) ? + peak_pciec_write_reg : peak_pci_write_reg; priv->read_reg = peak_pci_read_reg; - priv->write_reg = peak_pci_write_reg; priv->post_irq = peak_pci_post_irq; priv->can.clock.freq = PEAK_PCI_CAN_CLOCK; @@ -188,26 +606,40 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); + /* Create chain of SJA1000 devices */ + chan->prev_dev = pci_get_drvdata(pdev); + pci_set_drvdata(pdev, dev); + + /* + * PCAN-ExpressCard needs some support for leds. This must be + * done *before* register_sja1000dev() but *after* devices + * linkage + */ + if (pdev->device == PEAK_PCIEC_DEVICE_ID) { + err = peak_pciec_init(pdev, dev); + if (err) { + dev_warn(&pdev->dev, + "%s channel led won't blink (err %d)\n", + dev->name, err); + + /* handle board as a PCI card */ + priv->write_reg = peak_pci_write_reg; + } + } + err = register_sja1000dev(dev); if (err) { dev_err(&pdev->dev, "failed to register device\n"); + pci_set_drvdata(pdev, chan->prev_dev); free_sja1000dev(dev); goto failure_remove_channels; } - /* Create chain of SJA1000 devices */ - if (i == 0) - dev0 = dev; - else - chan->next_dev = dev; - dev_info(&pdev->dev, "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", dev->name, priv->reg_base, chan->cfg_base, dev->irq); } - pci_set_drvdata(pdev, dev0); - /* Enable interrupts */ writew(icr, cfg_base + PITA_ICR + 2); @@ -217,14 +649,18 @@ failure_remove_channels: /* Disable interrupts */ writew(0x0, cfg_base + PITA_ICR + 2); - for (dev = dev0; dev; dev = chan->next_dev) { + chan = NULL; + for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { unregister_sja1000dev(dev); free_sja1000dev(dev); priv = netdev_priv(dev); chan = priv->priv; - dev = chan->next_dev; } + /* free any PCIeC resources too */ + if (chan && chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + pci_iounmap(pdev, reg_base); failure_unmap_cfg_base: @@ -241,7 +677,7 @@ failure_disable_pci: static void __devexit peak_pci_remove(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); /* First device */ + struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ struct sja1000_priv *priv = netdev_priv(dev); struct peak_pci_chan *chan = priv->priv; void __iomem *cfg_base = chan->cfg_base; @@ -255,9 +691,14 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) dev_info(&pdev->dev, "removing device %s\n", dev->name); unregister_sja1000dev(dev); free_sja1000dev(dev); - dev = chan->next_dev; - if (!dev) + dev = chan->prev_dev; + + /* do that only for first channel */ + if (!dev) { + if (chan->pciec_card) + peak_pciec_remove(chan->pciec_card); break; + } priv = netdev_priv(dev); chan = priv->priv; } @@ -271,7 +712,7 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) } static struct pci_driver peak_pci_driver = { - .name = DRV_NAME, + .name = PEAK_PCI_DRV_NAME, .id_table = peak_pci_tbl, .probe = peak_pci_probe, .remove = __devexit_p(peak_pci_remove), -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 13:19 ` [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean @ 2012-01-31 13:25 ` Marc Kleine-Budde 2012-01-31 15:01 ` Stephane Grosjean 2012-01-31 22:54 ` Marc Kleine-Budde 1 sibling, 1 reply; 10+ messages in thread From: Marc Kleine-Budde @ 2012-01-31 13:25 UTC (permalink / raw) To: Stephane Grosjean; +Cc: Oliver Hartkopp, linux-can Mailing List [-- Attachment #1: Type: text/plain, Size: 841 bytes --] On 01/31/2012 02:19 PM, Stephane Grosjean wrote: > This patch adds the support of some new PEAK-System Technik sja1000 boards > (http://www.peak-system.com): > > PCAN-PCI Express (1 or 2 channels) > PCAN-ExpressCard (1 or 2 channels) > PCAN-miniPCI (1 or 2 channels) > > All of these boards are compliant with CAN specifications 2.0A (11-bit ID) and > 2.0B (29-bit ID). > > This patch also fixes the management of the channels list. Without looking further at the patch, please make it a seperate patch. A detailed review will follow. Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 262 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 13:25 ` Marc Kleine-Budde @ 2012-01-31 15:01 ` Stephane Grosjean 2012-01-31 15:16 ` Wolfgang Grandegger 2012-01-31 16:55 ` Marc Kleine-Budde 0 siblings, 2 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-01-31 15:01 UTC (permalink / raw) To: Marc Kleine-Budde; +Cc: Oliver Hartkopp, linux-can Mailing List Hi Marc, What do you mean please? You want me to post 4 patches? One for each new board, plus one for the channels list management? For 2 of the 3 boards, this is as simple as adding a line into the device ids table, excepting the ExpressCard for which I added the management of the funky leds. Thanks for any clarification. Stéphane Le 31/01/2012 14:25, Marc Kleine-Budde a écrit : > On 01/31/2012 02:19 PM, Stephane Grosjean wrote: >> This patch adds the support of some new PEAK-System Technik sja1000 boards >> (http://www.peak-system.com): >> >> PCAN-PCI Express (1 or 2 channels) >> PCAN-ExpressCard (1 or 2 channels) >> PCAN-miniPCI (1 or 2 channels) >> >> All of these boards are compliant with CAN specifications 2.0A (11-bit ID) and >> 2.0B (29-bit ID). >> >> This patch also fixes the management of the channels list. > Without looking further at the patch, please make it a seperate patch. A > detailed review will follow. > > > Marc > -- PEAK-System Technik GmbH, Otto-Roehm-Strasse 69, D-64293 Darmstadt Geschaeftsleitung: A.Gach/U.Wilhelm,St.Nr.:007/241/13586 FA Darmstadt HRB-9183 Darmstadt, Ust.IdNr.:DE 202220078, WEE-Reg.-Nr.: DE39305391 Tel.+49 (0)6151-817320 / Fax:+49 (0)6151-817329, info@peak-system.com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 15:01 ` Stephane Grosjean @ 2012-01-31 15:16 ` Wolfgang Grandegger 2012-01-31 15:26 ` Stephane Grosjean 2012-02-02 16:33 ` Stephane Grosjean 2012-01-31 16:55 ` Marc Kleine-Budde 1 sibling, 2 replies; 10+ messages in thread From: Wolfgang Grandegger @ 2012-01-31 15:16 UTC (permalink / raw) To: s.grosjean; +Cc: Marc Kleine-Budde, Oliver Hartkopp, linux-can Mailing List On 01/31/2012 04:01 PM, Stephane Grosjean wrote: > Hi Marc, > > What do you mean please? You want me to post 4 patches? One for each new > board, plus one for the channels list management? For 2 of the 3 boards, > this is as simple as adding a line into the device ids table, excepting > the ExpressCard for which I added the management of the funky leds. > Thanks for any clarification. I think he wants two separate patches, instead a series of patches. One patch for peak_pci and another for peak_pcmcia. Which does make sense as they do not belong together. Wolfgang. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 15:16 ` Wolfgang Grandegger @ 2012-01-31 15:26 ` Stephane Grosjean 2012-02-02 16:33 ` Stephane Grosjean 1 sibling, 0 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-01-31 15:26 UTC (permalink / raw) To: Wolfgang Grandegger Cc: Marc Kleine-Budde, Oliver Hartkopp, linux-can Mailing List I thought my serie of patches concerned the can/sja1000 subdir... But ok I see, I'll do these two distinct patches then will post that stuff again soon. Stéphane. Le 31/01/2012 16:16, Wolfgang Grandegger a écrit : > On 01/31/2012 04:01 PM, Stephane Grosjean wrote: >> Hi Marc, >> >> What do you mean please? You want me to post 4 patches? One for each new >> board, plus one for the channels list management? For 2 of the 3 boards, >> this is as simple as adding a line into the device ids table, excepting >> the ExpressCard for which I added the management of the funky leds. >> Thanks for any clarification. > I think he wants two separate patches, instead a series of patches. One > patch for peak_pci and another for peak_pcmcia. Which does make sense as > they do not belong together. > > Wolfgang. -- PEAK-System Technik GmbH, Otto-Roehm-Strasse 69, D-64293 Darmstadt Geschaeftsleitung: A.Gach/U.Wilhelm,St.Nr.:007/241/13586 FA Darmstadt HRB-9183 Darmstadt, Ust.IdNr.:DE 202220078, WEE-Reg.-Nr.: DE39305391 Tel.+49 (0)6151-817320 / Fax:+49 (0)6151-817329, info@peak-system.com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 15:16 ` Wolfgang Grandegger 2012-01-31 15:26 ` Stephane Grosjean @ 2012-02-02 16:33 ` Stephane Grosjean 1 sibling, 0 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-02-02 16:33 UTC (permalink / raw) To: Wolfgang Grandegger; +Cc: Marc Kleine-Budde, linux-can Mailing List Hi, I've got one question about some new patch(es) I'd like to post in the drivers/net/can/sja1000 subdir too, about a pcmcia driver. According to which commit should I create this new patch? I mean, I recently posted a patch to change the above Kconfig description. Will the new patch's Kconfig include these modifications or should I wait for some ack or else?.. Regards, Stéphane Le 31/01/2012 16:16, Wolfgang Grandegger a écrit : > On 01/31/2012 04:01 PM, Stephane Grosjean wrote: >> Hi Marc, >> >> What do you mean please? You want me to post 4 patches? One for each new >> board, plus one for the channels list management? For 2 of the 3 boards, >> this is as simple as adding a line into the device ids table, excepting >> the ExpressCard for which I added the management of the funky leds. >> Thanks for any clarification. > I think he wants two separate patches, instead a series of patches. One > patch for peak_pci and another for peak_pcmcia. Which does make sense as > they do not belong together. > > Wolfgang. -- PEAK-System Technik GmbH, Otto-Roehm-Strasse 69, D-64293 Darmstadt Geschaeftsleitung: A.Gach/U.Wilhelm,St.Nr.:007/241/13586 FA Darmstadt HRB-9183 Darmstadt, Ust.IdNr.:DE 202220078, WEE-Reg.-Nr.: DE39305391 Tel.+49 (0)6151-817320 / Fax:+49 (0)6151-817329, info@peak-system.com ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 15:01 ` Stephane Grosjean 2012-01-31 15:16 ` Wolfgang Grandegger @ 2012-01-31 16:55 ` Marc Kleine-Budde 1 sibling, 0 replies; 10+ messages in thread From: Marc Kleine-Budde @ 2012-01-31 16:55 UTC (permalink / raw) To: s.grosjean; +Cc: Oliver Hartkopp, linux-can Mailing List [-- Attachment #1: Type: text/plain, Size: 975 bytes --] On 01/31/2012 04:01 PM, Stephane Grosjean wrote: > Hi Marc, > > What do you mean please? You want me to post 4 patches? One for each new > board, plus one for the channels list management? For 2 of the 3 boards, > this is as simple as adding a line into the device ids table, excepting > the ExpressCard for which I added the management of the funky leds. > Thanks for any clarification. [...] Your patch states to fix a problem: >>> This patch also fixes the management of the channels list. >> Without looking further at the patch, please make it a seperate patch. A >> detailed review will follow. It would be better to fix the problem before adding new features. Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 262 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards 2012-01-31 13:19 ` [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean 2012-01-31 13:25 ` Marc Kleine-Budde @ 2012-01-31 22:54 ` Marc Kleine-Budde 1 sibling, 0 replies; 10+ messages in thread From: Marc Kleine-Budde @ 2012-01-31 22:54 UTC (permalink / raw) To: Stephane Grosjean; +Cc: Oliver Hartkopp, linux-can Mailing List [-- Attachment #1: Type: text/plain, Size: 22146 bytes --] On 01/31/2012 02:19 PM, Stephane Grosjean wrote: > This patch adds the support of some new PEAK-System Technik sja1000 boards > (http://www.peak-system.com): > > PCAN-PCI Express (1 or 2 channels) > PCAN-ExpressCard (1 or 2 channels) > PCAN-miniPCI (1 or 2 channels) > > All of these boards are compliant with CAN specifications 2.0A (11-bit ID) and > 2.0B (29-bit ID). > > This patch also fixes the management of the channels list. > > Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com> > --- > drivers/net/can/sja1000/peak_pci.c | 521 +++++++++++++++++++++++++++++++++--- > 1 files changed, 481 insertions(+), 40 deletions(-) > > diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c > index 2c7f503..02d94bd 100644 > --- a/drivers/net/can/sja1000/peak_pci.c > +++ b/drivers/net/can/sja1000/peak_pci.c > @@ -1,9 +1,9 @@ > /* > * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> > * > - * Derived from the PCAN project file driver/src/pcan_pci.c: > + * Derived from the PCAN project files driver/src/pcan_pci.c: > * > - * Copyright (C) 2001-2006 PEAK System-Technik GmbH > + * Copyright (C) 2001-2012 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 > @@ -13,12 +13,7 @@ > * 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> > @@ -26,46 +21,117 @@ > #include <linux/delay.h> > #include <linux/pci.h> > #include <linux/io.h> > +#include <linux/i2c.h> > +#include <linux/i2c-algo-bit.h> > #include <linux/can.h> > #include <linux/can/dev.h> > - > #include "sja1000.h" > > MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); > -MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI/PCIe cards"); > -MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe CAN card"); > +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); > +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe/PCIeC miniPCI CAN cards"); > MODULE_LICENSE("GPL v2"); > > -#define DRV_NAME "peak_pci" > - > -struct peak_pci_chan { > - void __iomem *cfg_base; /* Common for all channels */ > - struct net_device *next_dev; /* Chain of network devices */ > - u16 icr_mask; /* Interrupt mask for fast ack */ > -}; > +#define PEAK_PCI_DRV_NAME "peak_pci" > +#define PEAK_PCI_CHAN_MAX 4 > > #define PEAK_PCI_CAN_CLOCK (16000000 / 2) > > #define PEAK_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) > #define PEAK_PCI_OCR OCR_TX0_PUSHPULL > > -/* > - * Important PITA registers > - */ > +/* PITA registers */ > #define PITA_ICR 0x00 /* Interrupt control register */ > #define PITA_GPIOICR 0x18 /* GPIO interface control register */ > #define PITA_MISC 0x1C /* Miscellaneous register */ > > +/* GPIOICR byte access offsets */ > +#define PITA_GPOUT 0x18 /* GPx output value */ > +#define PITA_GPIN 0x19 /* GPx input value */ > +#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */ > + > +/* I2C GP bits */ > +#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */ > +#define PITA_GPIN_SDA 0x04 /* Serial DAta line */ > + > +/* LEDs control */ > +#define PCA9553_1_SLAVEADDR (0xC4 >> 1) > + > +/* PCA9553 LS0 fields values */ > +enum { > + PCA9553_LOW, > + PCA9553_HIGHZ, > + PCA9553_PWM0, > + PCA9553_PWM1 > +}; > + > +#define PCA9553_ON PCA9553_LOW > +#define PCA9553_OFF PCA9553_HIGHZ > +#define PCA9553_SLOW PCA9553_PWM0 > +#define PCA9553_FAST PCA9553_PWM1 > + > +#define PCA9553_LED(c) (1 << (c)) > +#define PCA9553_LED_STATE(s, c) ((s) << ((c) << 1)) > + > +#define PCA9553_LED_ON(c) PCA9553_LED_STATE(PCA9553_ON, c) > +#define PCA9553_LED_OFF(c) PCA9553_LED_STATE(PCA9553_OFF, c) > +#define PCA9553_LED_SLOW(c) PCA9553_LED_STATE(PCA9553_SLOW, c) > +#define PCA9553_LED_FAST(c) PCA9553_LED_STATE(PCA9553_FAST, c) > +#define PCA9553_LED_MASK(c) PCA9553_LED_STATE(0x03, c) > + > +#define PCA9553_LED_OFF_ALL (PCA9553_LED_OFF(0) | PCA9553_LED_OFF(1)) > + > +#define PCA9553_LS0_INIT 0x40 /* initial value (!= from 0x00) */ > + > +#define PCA9553_LS0_NONE 0x45 /* off value */ > + > #define PEAK_PCI_CFG_SIZE 0x1000 /* Size of the config PCI bar */ > #define PEAK_PCI_CHAN_SIZE 0x0400 /* Size used by the channel */ > > +/* PCI stuff */ > #define PEAK_PCI_VENDOR_ID 0x001C /* The PCI device and vendor IDs */ > #define PEAK_PCI_DEVICE_ID 0x0001 /* for PCI/PCIe slot cards */ > +#define PEAK_PCIEC_DEVICE_ID 0x0002 /* for ExpressCard slot cards */ > +#define PEAK_PCIE_DEVICE_ID 0x0003 /* for nextgen PCIe slot cards */ > +#define PEAK_MPCI_DEVICE_ID 0x0008 /* The miniPCI slot cards */ > + > +/* PCAN-PCIeC specific (leds management) */ > +struct peak_pciec_chan { > + struct net_device *netdev; > + unsigned long prev_rx_bytes; > + unsigned long prev_tx_bytes; > +}; > > -static const u16 peak_pci_icr_masks[] = {0x02, 0x01, 0x40, 0x80}; > +struct peak_pciec_card { > + void __iomem *cfg_base; /* Common for all channels */ > + void __iomem *reg_base; /* first channel base address */ > + u8 led_cache; /* leds state cache */ > + > + /* PCIExpressCard i2c data */ > + struct i2c_algo_bit_data i2c_bit; > + struct i2c_adapter led_chip; > + > + struct timer_list led_timer; /* activity timer */ > + int chan_count; > + struct peak_pciec_chan channel[PEAK_PCI_CHAN_MAX]; > +}; > + > +struct peak_pci_chan { > + void __iomem *cfg_base; /* Common for all channels */ > + struct net_device *prev_dev; /* Chain of network devices */ > + u16 icr_mask; /* Interrupt mask for fast ack */ Use tabs instead of spaces to align the comments. > + struct peak_pciec_card *pciec_card; /* only for PCIeC */ > +}; > + > +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { > + 0x02, 0x01, 0x40, 0x80 > +}; > > static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = { > {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, > + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, > + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, > + {PEAK_PCI_VENDOR_ID, PEAK_MPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, > {0,} > }; > > @@ -85,20 +151,370 @@ static void peak_pci_write_reg(const struct sja1000_priv *priv, > static void peak_pci_post_irq(const struct sja1000_priv *priv) > { > struct peak_pci_chan *chan = priv->priv; > - u16 icr; > + u16 icr = readw(chan->cfg_base + PITA_ICR); > > /* Select and clear in PITA stored interrupt */ > - icr = readw(chan->cfg_base + PITA_ICR); > if (icr & chan->icr_mask) > writew(chan->icr_mask, chan->cfg_base + PITA_ICR); > } > > +static inline void pita_set_scl_highz(struct peak_pciec_card *card) > +{ > + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SCL; > + writeb(gp_outen, card->cfg_base + PITA_GPOEN); > +} > + > +static inline void pita_set_sda_highz(struct peak_pciec_card *card) > +{ > + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SDA; > + writeb(gp_outen, card->cfg_base + PITA_GPOEN); > +} > + > +static void peak_pciec_init_pita_gpio(struct peak_pciec_card *card) > +{ > + /* raise SCL & SDA GPIOs to high-Z */ > + pita_set_scl_highz(card); > + pita_set_sda_highz(card); > +} > + > +static void pita_setsda(void *data, int state) > +{ > + struct peak_pciec_card *card = (struct peak_pciec_card *)data; > + u8 gp_out, gp_outen; > + > + /* set output sda always to 0 */ > + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SDA; > + writeb(gp_out, card->cfg_base + PITA_GPOUT); > + > + /* control output sda with GPOEN */ > + gp_outen = readb(card->cfg_base + PITA_GPOEN); > + if (state) > + gp_outen &= ~PITA_GPIN_SDA; > + else > + gp_outen |= PITA_GPIN_SDA; > + > + writeb(gp_outen, card->cfg_base + PITA_GPOEN); > +} > + > +static void pita_setscl(void *data, int state) > +{ > + struct peak_pciec_card *card = (struct peak_pciec_card *)data; > + u8 gp_out, gp_outen; > + > + /* set output scl always to 0 */ > + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SCL; > + writeb(gp_out, card->cfg_base + PITA_GPOUT); > + > + /* control output scl with GPOEN */ > + gp_outen = readb(card->cfg_base + PITA_GPOEN); > + if (state) > + gp_outen &= ~PITA_GPIN_SCL; > + else > + gp_outen |= PITA_GPIN_SCL; > + > + writeb(gp_outen, card->cfg_base + PITA_GPOEN); > +} > + > +static int pita_getsda(void *data) > +{ > + struct peak_pciec_card *card = (struct peak_pciec_card *)data; > + > + /* set tristate */ > + pita_set_sda_highz(card); > + > + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SDA) ? 1 : 0; > +} > + > +static int pita_getscl(void *data) > +{ > + struct peak_pciec_card *card = (struct peak_pciec_card *)data; > + > + /* set tristate */ > + pita_set_scl_highz(card); > + > + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SCL) ? 1 : 0; > +} > + > +/* > + * write commands to the LED chip though the I2C-bus of the PCAN-PCIeC > + */ > +static int peak_pciec_write_pca9553(struct peak_pciec_card *card, > + u8 offset, u8 data) > +{ > + u8 buffer[2] = { > + offset, > + data > + }; > + struct i2c_msg msg = { > + .addr = PCA9553_1_SLAVEADDR, > + .len = 2, > + .buf = buffer, > + }; > + int ret; > + > + /* cache led mask */ > + if ((offset == 5) && (data == card->led_cache)) > + return 0; > + > + ret = i2c_transfer(&card->led_chip, &msg, 1); > + if (ret < 0) > + return ret; > + > + if (offset == 5) > + card->led_cache = data; > + > + return 0; > +} > + > +/* > + * timer callback used to control the LEDs > + */ > +static void peak_pciec_led_timer(unsigned long arg) > +{ > + struct peak_pciec_card *card = (struct peak_pciec_card *)arg; > + struct net_device *netdev; > + u8 new_led = card->led_cache; > + int i, up_count = 0; > + > + /* first check what is to do */ > + for (i = 0; i < card->chan_count; i++) { > + /* default is: not configured */ > + new_led &= ~PCA9553_LED_MASK(i); > + new_led |= PCA9553_LED_ON(i); > + > + netdev = card->channel[i].netdev; > + if (!netdev || !(netdev->flags & IFF_UP)) > + continue; > + > + up_count++; > + > + /* no activity (but configured) */ > + new_led &= ~PCA9553_LED_MASK(i); > + new_led |= PCA9553_LED_SLOW(i); > + > + /* if bytes counters changed, set fast blinking led */ > + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { > + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; > + new_led &= ~PCA9553_LED_MASK(i); > + new_led |= PCA9553_LED_FAST(i); > + } > + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { > + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; > + new_led &= ~PCA9553_LED_MASK(i); > + new_led |= PCA9553_LED_FAST(i); > + } > + } > + > + /* check if LS0 settings changed, only update i2c if so */ > + peak_pciec_write_pca9553(card, 5, new_led); > + > + /* restart timer (except if no more configured channels) */ > + if (up_count) > + mod_timer(&card->led_timer, jiffies + HZ); > +} > + > +/* > + * set LEDs blinking state > + */ > +static void peak_pciec_set_leds(struct peak_pciec_card *card, u8 led_mask, u8 s) > +{ > + u8 new_led = card->led_cache; > + int i; > + > + /* first check what is to do */ > + for (i = 0; i < card->chan_count; i++) > + if (led_mask & PCA9553_LED(i)) { > + new_led &= ~PCA9553_LED_MASK(i); > + new_led |= PCA9553_LED_STATE(s, i); > + } > + > + /* check if LS0 settings changed, only update i2c if so */ > + peak_pciec_write_pca9553(card, 5, new_led); > +} > + > +/* > + * start one second timer to control LEDs > + */ > +static void peak_pciec_start_led_timer(struct peak_pciec_card *card) > +{ > + if (!timer_pending(&card->led_timer)) > + mod_timer(&card->led_timer, jiffies + HZ); > +} > + > +/* > + * stop LEDs timer > + */ > +static void peak_pciec_stop_led_timer(struct peak_pciec_card *card) > +{ > + del_timer_sync(&card->led_timer); > +} > + > +/* > + * initialize the PCA9553 4-bit I2C-bus LED chip > + */ > +static int peak_pciec_init_leds(struct peak_pciec_card *card) > +{ > + int err; > + > + /* prescaler for frequency 0: "SLOW" = 1 Hz = "44" */ > + err = peak_pciec_write_pca9553(card, 1, 44 / 1); > + if (err) > + return err; > + > + /* duty cycle 0: 50% */ > + err = peak_pciec_write_pca9553(card, 2, 0x80); > + if (err) > + return err; > + > + /* prescaler for frequency 1: "FAST" = 5 Hz */ > + err = peak_pciec_write_pca9553(card, 3, 44 / 5); > + if (err) > + return err; > + > + /* duty cycle 1: 50% */ > + err = peak_pciec_write_pca9553(card, 4, 0x80); > + if (err) > + return err; > + > + /* switch LEDs to initial state */ > + return peak_pciec_write_pca9553(card, 5, PCA9553_LS0_INIT); > +} > + > +/* > + * restore LEDs state to off peak_pciec_leds_exit > + */ > +static void peak_pciec_leds_exit(struct peak_pciec_card *card) > +{ > + /* switch LEDs to off */ > + peak_pciec_write_pca9553(card, 5, PCA9553_LED_OFF_ALL); > +} > + > +/* > + * normal write sja1000 register method overloaded to catch when controller > + * is started or stopped, to control leds > + */ > +static void peak_pciec_write_reg(const struct sja1000_priv *priv, > + int port, u8 val) > +{ > + struct peak_pci_chan *chan = priv->priv; > + struct peak_pciec_card *card = chan->pciec_card; > + int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE; > + > + /* sja1000 register changes control the leds state */ > + if (port == REG_MOD) > + switch (val) { > + case MOD_RM: > + /* Reset Mode: set led on */ > + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_ON); > + break; > + case 0x00: > + /* Normal Mode: led slow blinking and start led timer */ > + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_SLOW); > + peak_pciec_start_led_timer(card); > + break; > + default: > + break; > + } > + > + /* call base function */ > + peak_pci_write_reg(priv, port, val); > +} > + > +static struct i2c_algo_bit_data peak_pciec_i2c_bit_ops = { > + .setsda = pita_setsda, > + .setscl = pita_setscl, > + .getsda = pita_getsda, > + .getscl = pita_getscl, > + .udelay = 10, > + .timeout = HZ, > +}; > + > +static int peak_pciec_init(struct pci_dev *pdev, struct net_device *dev) > +{ > + struct sja1000_priv *priv = netdev_priv(dev); > + struct peak_pci_chan *chan = priv->priv; > + struct peak_pciec_card *card; > + int err; > + > + /* copy i2c object address from 1st channel */ > + if (chan->prev_dev) { > + struct sja1000_priv *prev_priv = netdev_priv(chan->prev_dev); > + struct peak_pci_chan *prev_chan = prev_priv->priv; > + > + card = prev_chan->pciec_card; > + if (!card) > + return -ENODEV; > + > + /* channel is the first one: do the init part */ > + } else { > + /* create the bit banging I2C adapter structure */ > + card = kzalloc(sizeof(struct peak_pciec_card), GFP_KERNEL); > + if (!card) { > + dev_warn(&pdev->dev, > + "failed allocating memory for leds chip\n"); > + return -ENOMEM; > + } > + > + card->cfg_base = chan->cfg_base; > + card->reg_base = priv->reg_base; > + > + card->led_chip.owner = THIS_MODULE; > + card->led_chip.dev.parent = &pdev->dev; > + card->led_chip.algo_data = &card->i2c_bit; > + strncpy(card->led_chip.name, "peak_i2c", > + sizeof(card->led_chip.name)); > + > + card->i2c_bit = peak_pciec_i2c_bit_ops; > + card->i2c_bit.udelay = 10; > + card->i2c_bit.timeout = HZ; > + card->i2c_bit.data = card; > + > + peak_pciec_init_pita_gpio(card); > + > + err = i2c_bit_add_bus(&card->led_chip); > + if (err) { > + dev_warn(&pdev->dev, "i2c init failed\n"); > + peak_pciec_init_pita_gpio(card); > + kfree(card); > + return err; > + } > + > + err = peak_pciec_init_leds(card); > + if (err) { > + dev_warn(&pdev->dev, "leds hardware init failed\n"); > + i2c_del_adapter(&card->led_chip); > + peak_pciec_init_pita_gpio(card); > + kfree(card); > + return err; Can you switch this code to goto style error handling, please? > + } > + > + /* init the timer which controls the leds */ > + init_timer(&card->led_timer); > + card->led_timer.function = peak_pciec_led_timer; > + card->led_timer.data = (unsigned long)card; > + } > + > + chan->pciec_card = card; > + card->channel[card->chan_count++].netdev = dev; > + > + return 0; > +} > + > +static void peak_pciec_remove(struct peak_pciec_card *card) > +{ > + peak_pciec_stop_led_timer(card); > + peak_pciec_leds_exit(card); > + i2c_del_adapter(&card->led_chip); > + peak_pciec_init_pita_gpio(card); > + kfree(card); > +} > + > static int __devinit peak_pci_probe(struct pci_dev *pdev, > const struct pci_device_id *ent) > { > struct sja1000_priv *priv; > struct peak_pci_chan *chan; > - struct net_device *dev, *dev0 = NULL; > + struct net_device *dev; > void __iomem *cfg_base, *reg_base; > u16 sub_sys_id, icr; > int i, err, channels; > @@ -107,7 +523,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, > if (err) > return err; > > - err = pci_request_regions(pdev, DRV_NAME); > + err = pci_request_regions(pdev, PEAK_PCI_DRV_NAME); > if (err) > goto failure_disable_pci; > > @@ -168,8 +584,10 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, > chan->cfg_base = cfg_base; > priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE; > > + /* PCAN-ExpressCard needs some support for leds */ > + priv->write_reg = (pdev->device == PEAK_PCIEC_DEVICE_ID) ? > + peak_pciec_write_reg : peak_pci_write_reg; > priv->read_reg = peak_pci_read_reg; > - priv->write_reg = peak_pci_write_reg; > priv->post_irq = peak_pci_post_irq; > > priv->can.clock.freq = PEAK_PCI_CAN_CLOCK; > @@ -188,26 +606,40 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, > > SET_NETDEV_DEV(dev, &pdev->dev); > > + /* Create chain of SJA1000 devices */ > + chan->prev_dev = pci_get_drvdata(pdev); > + pci_set_drvdata(pdev, dev); > + > + /* > + * PCAN-ExpressCard needs some support for leds. This must be > + * done *before* register_sja1000dev() but *after* devices > + * linkage > + */ > + if (pdev->device == PEAK_PCIEC_DEVICE_ID) { > + err = peak_pciec_init(pdev, dev); > + if (err) { > + dev_warn(&pdev->dev, > + "%s channel led won't blink (err %d)\n", > + dev->name, err); > + > + /* handle board as a PCI card */ I read the code and understand why you can handle a PCIe card like a PCI card. The PCIe card has additional support for the LEDs, this should be refleced in the comment. > + priv->write_reg = peak_pci_write_reg; > + } > + } > + > err = register_sja1000dev(dev); > if (err) { > dev_err(&pdev->dev, "failed to register device\n"); > + pci_set_drvdata(pdev, chan->prev_dev); > free_sja1000dev(dev); > goto failure_remove_channels; > } > > - /* Create chain of SJA1000 devices */ > - if (i == 0) > - dev0 = dev; > - else > - chan->next_dev = dev; > - > dev_info(&pdev->dev, > "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", > dev->name, priv->reg_base, chan->cfg_base, dev->irq); > } > > - pci_set_drvdata(pdev, dev0); > - > /* Enable interrupts */ > writew(icr, cfg_base + PITA_ICR + 2); > > @@ -217,14 +649,18 @@ failure_remove_channels: > /* Disable interrupts */ > writew(0x0, cfg_base + PITA_ICR + 2); > > - for (dev = dev0; dev; dev = chan->next_dev) { > + chan = NULL; > + for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { > unregister_sja1000dev(dev); > free_sja1000dev(dev); > priv = netdev_priv(dev); > chan = priv->priv; > - dev = chan->next_dev; > } > > + /* free any PCIeC resources too */ > + if (chan && chan->pciec_card) > + peak_pciec_remove(chan->pciec_card); > + > pci_iounmap(pdev, reg_base); > > failure_unmap_cfg_base: > @@ -241,7 +677,7 @@ failure_disable_pci: > > static void __devexit peak_pci_remove(struct pci_dev *pdev) > { > - struct net_device *dev = pci_get_drvdata(pdev); /* First device */ > + struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ > struct sja1000_priv *priv = netdev_priv(dev); > struct peak_pci_chan *chan = priv->priv; > void __iomem *cfg_base = chan->cfg_base; > @@ -255,9 +691,14 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) > dev_info(&pdev->dev, "removing device %s\n", dev->name); > unregister_sja1000dev(dev); > free_sja1000dev(dev); > - dev = chan->next_dev; > - if (!dev) > + dev = chan->prev_dev; > + > + /* do that only for first channel */ > + if (!dev) { > + if (chan->pciec_card) > + peak_pciec_remove(chan->pciec_card); > break; > + } > priv = netdev_priv(dev); > chan = priv->priv; > } > @@ -271,7 +712,7 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) > } > > static struct pci_driver peak_pci_driver = { > - .name = DRV_NAME, > + .name = PEAK_PCI_DRV_NAME, > .id_table = peak_pci_tbl, > .probe = peak_pci_probe, > .remove = __devexit_p(peak_pci_remove), Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 262 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/2] can/sja1000: Add support of PEAK-System PCMCIA board 2012-01-31 13:19 [PATCH 0/2] can/sja1000: Add/upgrade PEAK-System sja1000 driver Stephane Grosjean 2012-01-31 13:19 ` [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean @ 2012-01-31 13:19 ` Stephane Grosjean 1 sibling, 0 replies; 10+ messages in thread From: Stephane Grosjean @ 2012-01-31 13:19 UTC (permalink / raw) To: Oliver Hartkopp; +Cc: linux-can Mailing List, Stephane Grosjean This patch adds the support of the PCAN-PC Card (PCMCIA) board from PEAK-System Technik (http://www.peak-system.com). The PCAN-PC Card board is sja1000 based and exists in 1 or 2 channels and is compliant with CAN specifications 2.0A (11-bit ID) and 2.0B (29-bit ID). Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com> --- drivers/net/can/sja1000/Kconfig | 9 + drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/peak_pcmcia.c | 721 +++++++++++++++++++++++++++++++++ 3 files changed, 731 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/peak_pcmcia.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index 36e9d59..0c8309b 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -35,6 +35,15 @@ config CAN_EMS_PCMCIA This driver is for the one or two channel CPC-CARD cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). +config CAN_PEAK_PCMCIA + tristate "PEAK PCAN PC-Card" + depends on PCMCIA + ---help--- + This driver is for the PCAN PC-Card PCMCIA adapter (1 or 2 channels) + from PEAK Systems (http://www.peak-system.com). To compile this + driver as a module, choose M here: the module will be called + peak_pcmcia. + config CAN_EMS_PCI tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card" depends on PCI diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 0604f24..b3d05cb 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o +obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c new file mode 100644 index 0000000..6ba58c2 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -0,0 +1,721 @@ +/* + * CAN driver for PEAK-System PCAN-PC Card + * Derived from the PCAN project file driver/src/pcan_pccard.c + * + * Copyright (C) 2006-2009 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/io.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include "sja1000.h" + +/* PEAK-System PCMCIA driver name */ +#define PCC_NAME "peak_pcmcia" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("CAN driver for PEAK-System PCAN-PC Cards"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN-PC Card"); + +#define PCC_CHAN_MAX 2 + +#define PCC_CAN_CLOCK (16000000 / 2) + +#define PCC_MANF_ID 0x0377 +#define PCC_CARD_ID 0x0001 + +#define PCC_CHAN_SIZE 0x20 +#define PCC_CHAN_OFF(c) ((c) * PCC_CHAN_SIZE) +#define PCC_COMN_OFF (PCC_CHAN_OFF(PCC_CHAN_MAX)) +#define PCC_COMN_SIZE 0x40 + +/* common area registers */ +#define PCC_CCR 0x00 +#define PCC_CSR 0x02 +#define PCC_CPR 0x04 +#define PCC_SPI_DIR 0x06 +#define PCC_SPI_DOR 0x08 +#define PCC_SPI_ADR 0x0a +#define PCC_SPI_IR 0x0c +#define PCC_FW_MAJOR 0x10 +#define PCC_FW_MINOR 0x12 + +/* CCR bits */ +#define PCC_CCR_CLK_16 0x00 +#define PCC_CCR_CLK_10 0x01 +#define PCC_CCR_CLK_21 0x02 +#define PCC_CCR_CLK_8 0x03 +#define PCC_CCR_CLK_MASK PCC_CCR_CLK_8 + +#define PCC_CCR_RST_CHAN(c) (0x01 << ((c) + 2)) +#define PCC_CCR_RST_ALL (PCC_CCR_RST_CHAN(0) | PCC_CCR_RST_CHAN(1)) +#define PCC_CCR_RST_MASK PCC_CCR_RST_ALL + +/* led selection bits */ +#define PCC_LED(c) (1 << (c)) +#define PCC_LED_ALL (PCC_LED(0) | PCC_LED(1)) + +/* led state value */ +#define PCC_LED_ON 0x00 +#define PCC_LED_FAST 0x01 +#define PCC_LED_SLOW 0x02 +#define PCC_LED_OFF 0x03 + +#define PCC_CCR_LED_CHAN(s, c) ((s) << (((c) + 2) << 1)) + +#define PCC_CCR_LED_ON_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_ON, c) +#define PCC_CCR_LED_FAST_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_FAST, c) +#define PCC_CCR_LED_SLOW_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_SLOW, c) +#define PCC_CCR_LED_OFF_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_OFF, c) +#define PCC_CCR_LED_MASK_CHAN(c) PCC_CCR_LED_OFF_CHAN(c) +#define PCC_CCR_LED_OFF_ALL (PCC_CCR_LED_OFF_CHAN(0) | \ + PCC_CCR_LED_OFF_CHAN(1)) +#define PCC_CCR_LED_MASK PCC_CCR_LED_OFF_ALL + +#define PCC_CCR_INIT (PCC_CCR_CLK_16 | PCC_CCR_RST_ALL | PCC_CCR_LED_OFF_ALL) + +/* CSR bits */ +#define PCC_CSR_SPI_BUSY 0x04 + +#define PCC_SPI_MAX_BUSY_WAIT_MS 3 /* time waiting for spi !busy */ + +/* max count of reading the SPI status register waiting for a change */ +#define PCC_WRITE_MAX_LOOP 1000 + +/* max nb of int handled by that isr in one shot (prevent from infinite loop) */ +#define PCC_ISR_MAX_LOOP 10 + +/* EEPROM chip instruction set */ +/* note: EEPROM Read/Write instructions include A8 bit */ +#define PCC_EEP_WRITE(a) (0x02 | (((a) & 0x100) >> 5)) +#define PCC_EEP_READ(a) (0x03 | (((a) & 0x100) >> 5)) +#define PCC_EEP_WRDI 0x04 /* EEPROM Write Disable */ +#define PCC_EEP_RDSR 0x05 /* EEPROM Read Status Register */ +#define PCC_EEP_WREN 0x06 /* EEPROM Write Enable */ + +/* EEPROM Status Register bits */ +#define PCC_EEP_SR_WEN 0x02 /* EEPROM SR Write Enable bit */ +#define PCC_EEP_SR_WIP 0x01 /* EEPROM SR Write In Progress bit */ + +/* + * 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 PCC_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 PCC_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +struct pcan_channel { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +/* PCAN-PC Card private structure */ +struct pcan_pccard { + struct pcmcia_device *pdev; + int chan_count; + struct pcan_channel channel[PCC_CHAN_MAX]; + u8 ccr; + void __iomem *ioport_addr; + struct timer_list led_timer; +}; + +static struct pcmcia_device_id pcan_table[] = { + PCMCIA_DEVICE_MANF_CARD(PCC_MANF_ID, PCC_CARD_ID), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, pcan_table); + +static void pcan_set_leds(struct pcan_pccard *card, u8 mask, u8 state); + +/* + * start timer which controls leds state + */ +static void pcan_start_led_timer(struct pcan_pccard *card) +{ + if (!timer_pending(&card->led_timer)) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * stop the timer which controls leds state + */ +static void pcan_stop_led_timer(struct pcan_pccard *card) +{ + del_timer_sync(&card->led_timer); +} + +/* + * read a sja1000 register + */ +static u8 pcan_read_canreg(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +/* + * write a sja1000 register + */ +static void pcan_write_canreg(const struct sja1000_priv *priv, int port, u8 v) +{ + struct pcan_pccard *card = priv->priv; + int c = (priv->reg_base - card->ioport_addr) / PCC_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (v) { + case MOD_RM: + /* Reset Mode: set led on */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_SLOW); + pcan_start_led_timer(card); + break; + default: + break; + } + + iowrite8(v, priv->reg_base + port); +} + +/* + * read a register from the common area + */ +static u8 pcan_read_reg(struct pcan_pccard *card, int port) +{ + return ioread8(card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * write a register into the common area + */ +static void pcan_write_reg(struct pcan_pccard *card, int port, u8 v) +{ + /* cache ccr value */ + if (port == PCC_CCR) { + if (card->ccr == v) + return; + card->ccr = v; + } + + iowrite8(v, card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * wait for SPI engine while it is busy + */ +static int pcan_wait_spi_busy(struct pcan_pccard *card) +{ + unsigned long timeout = jiffies + + msecs_to_jiffies(PCC_SPI_MAX_BUSY_WAIT_MS) + 1; + + /* be sure to read status at least once after sleeping */ + while (pcan_read_reg(card, PCC_CSR) & PCC_CSR_SPI_BUSY) { + if (jiffies >= timeout) + return -EBUSY; + schedule(); + } + + return 0; +} + +/* + * write data in device eeprom + */ +static int pcan_write_eeprom(struct pcan_pccard *card, u16 addr, u8 v) +{ + u8 status; + int err, i; + + /* write instruction enabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WREN); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait until write enabled */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write enable bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (status & PCC_EEP_SR_WEN) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting to be allowed to write in eeprom\n"); + return -EIO; + } + + /* set address and data */ + pcan_write_reg(card, PCC_SPI_ADR, addr & 0xff); + pcan_write_reg(card, PCC_SPI_DOR, v); + + /* + * write instruction with bit[3] set according to address value: + * if addr refers to upper half of the memory array: bit[3] = 1 + */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRITE(addr)); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait while write in progress */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write in progress bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (!(status & PCC_EEP_SR_WIP)) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting for write in eeprom to complete\n"); + return -EIO; + } + + /* write instruction disabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRDI); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + return 0; + +we_spi_err: + dev_err(&card->pdev->dev, + "stop waiting (spi engine always busy) err %d\n", err); + + return err; +} + +static void pcan_set_leds(struct pcan_pccard *card, u8 led_mask, u8 state) +{ + u8 ccr = card->ccr; + int i; + + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCC_LED(i)) { + /* clear corresponding led bits in ccr */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + /* then set new bits */ + ccr |= PCC_CCR_LED_CHAN(state, i); + } + + /* real write only if something has changed in ccr */ + pcan_write_reg(card, PCC_CCR, ccr); +} + +/* + * enable/disable CAN connectors power + */ +static inline void pcan_set_can_power(struct pcan_pccard *card, int onoff) +{ + int err = pcan_write_eeprom(card, 0, (onoff) ? 1 : 0); + if (err) + dev_err(&card->pdev->dev, + "failed setting power %s to can connectors (err %d)\n", + (onoff) ? "on" : "off", err); +} + +/* + * set leds state according to channel activity + */ +static void pcan_led_timer(unsigned long arg) +{ + struct pcan_pccard *card = (struct pcan_pccard *)arg; + struct net_device *netdev; + u8 ccr = card->ccr; + int i, up_count = 0; + + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_ON_CHAN(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_SLOW_CHAN(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + } + + /* write the new leds state */ + pcan_write_reg(card, PCC_CCR, ccr); + + /* restart timer (except if no more configured channels) */ + if (up_count) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * interrupt service routine + */ +static irqreturn_t pcan_isr(int irq, void *dev_id) +{ + struct pcan_pccard *card = dev_id; + int irq_handled; + + /* prevent from infinite loop */ + for (irq_handled = 0; irq_handled < PCC_ISR_MAX_LOOP; irq_handled++) { + /* handle shared interrupt and next pass */ + int nothing_to_handle = 1; + int i; + + /* check interrupt for each channel */ + for (i = 0; i < card->chan_count; i++) { + /* be sure the netdevice always exists */ + struct net_device *netdev = card->channel[i].netdev; + if (!netdev) + continue; + + /* + * if some interrupt from the controller handled, + * should check whether all have been handled or if have + * been stopped because of SJA1000_MAX_IRQ + */ + if (sja1000_interrupt(irq, netdev) == IRQ_HANDLED) + nothing_to_handle = 0; + } + + if (nothing_to_handle) + break; + } + + return (irq_handled) ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * free all resources used by the channels and switch off leds and can power + */ +static void pcan_free_channels(struct pcan_pccard *card) +{ + int i; + u8 led_mask = 0; + + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + char name[IFNAMSIZ]; + + led_mask |= PCC_LED(i); + + netdev = card->channel[i].netdev; + if (!netdev) + continue; + + strncpy(name, netdev->name, IFNAMSIZ); + unregister_sja1000dev(netdev); + free_sja1000dev(netdev); + + dev_info(&card->pdev->dev, "%s removed\n", name); + } + + if (pcmcia_dev_present(card->pdev)) { + pcan_set_leds(card, led_mask, PCC_LED_OFF); + pcan_set_can_power(card, 0); + } + + ioport_unmap(card->ioport_addr); +} + +/* + * check if a CAN controller is present at the specified location + */ +static inline int pcan_check_channel(struct sja1000_priv *priv) +{ + /* make sure SJA1000 is in reset mode */ + pcan_write_canreg(priv, REG_MOD, 1); + pcan_write_canreg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + if (pcan_read_canreg(priv, REG_CDR) == CDR_PELICAN) + return 1; + + return 0; +} + +static int pcan_add_channels(struct pcan_pccard *card) +{ + struct pcmcia_device *pdev = card->pdev; + int i, err; + u8 ccr = PCC_CCR_INIT; + + /* init common registers (reset channels and leds off) */ + card->ccr = ~ccr; + pcan_write_reg(card, PCC_CCR, ccr); + + /* wait 2ms before unresetting channels */ + mdelay(2); + + ccr &= ~PCC_CCR_RST_ALL; + pcan_write_reg(card, PCC_CCR, ccr); + + /* create one network device per channel detected */ + for (i = 0; i < ARRAY_SIZE(card->channel); i++) { + struct net_device *netdev; + struct sja1000_priv *priv; + + netdev = alloc_sja1000dev(0); + if (!netdev) { + err = -ENOMEM; + break; + } + + /* update linkages */ + priv = netdev_priv(netdev); + priv->priv = card; + SET_NETDEV_DEV(netdev, &pdev->dev); + + priv->irq_flags = IRQF_SHARED; + netdev->irq = pdev->irq; + priv->reg_base = card->ioport_addr + PCC_CHAN_OFF(i); + + /* check if channel is present */ + if (!pcan_check_channel(priv)) { + dev_err(&pdev->dev, "channel %d not present\n", i); + free_sja1000dev(netdev); + continue; + } + + priv->read_reg = pcan_read_canreg; + priv->write_reg = pcan_write_canreg; + priv->can.clock.freq = PCC_CAN_CLOCK; + priv->ocr = PCC_OCR; + priv->cdr = PCC_CDR; + + /* Neither a slave device distributes the clock */ + if (i > 0) + priv->cdr |= CDR_CLK_OFF; + + priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER; + + /* register SJA1000 device */ + err = register_sja1000dev(netdev); + if (err) { + free_sja1000dev(netdev); + continue; + } + + card->channel[i].netdev = netdev; + card->chan_count++; + + /* set corresponding led on in the new ccr */ + ccr &= ~PCC_CCR_LED_OFF_CHAN(i); + + dev_info(&pdev->dev, + "registered %s on channel %d at 0x%p irq %d\n", + netdev->name, i, priv->reg_base, pdev->irq); + } + + /* write new ccr (change leds state) */ + pcan_write_reg(card, PCC_CCR, ccr); + + return err; +} + +static int pcan_conf_check(struct pcmcia_device *pdev, void *priv_data) +{ + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; /* only */ + pdev->io_lines = 10; + + /* This reserves IO space but doesn't actually enable it */ + return pcmcia_request_io(pdev); +} + +/* + * free all resources used by the device + */ +static void pcan_free(struct pcmcia_device *pdev) +{ + if (!pdev->priv) + return; + + pcan_stop_led_timer(pdev->priv); + free_irq(pdev->irq, pdev->priv); + pcan_free_channels(pdev->priv); + + kfree(pdev->priv); + pdev->priv = NULL; +} + +/* + * setup PCMCIA socket and probe for PEAK-System PC-CARD + */ +static int __devinit pcan_probe(struct pcmcia_device *pdev) +{ + struct pcan_pccard *card; + int err; + + pdev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + err = pcmcia_loop_config(pdev, pcan_conf_check, NULL); + if (err) { + dev_err(&pdev->dev, "pcmcia_loop_config() error %d\n", err); + goto probe_err_1; + } + + dev_info(&pdev->dev, "new PEAK-System pcmcia card detected: %s %s:\n", + pdev->prod_id[0] ? pdev->prod_id[0] : "PEAK-System", + pdev->prod_id[1] ? pdev->prod_id[1] : "PCAN-PC Card"); + + if (!pdev->irq) { + dev_err(&pdev->dev, "no irq assigned\n"); + err = -ENODEV; + goto probe_err_1; + } + + err = pcmcia_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "pcmcia_enable_device failed err=%d\n", + err); + goto probe_err_1; + } + + card = kzalloc(sizeof(struct pcan_pccard), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, "couldn't allocated card memory\n"); + err = -ENOMEM; + goto probe_err_2; + } + + card->pdev = pdev; + pdev->priv = card; + + /* sja1000 api uses iomem */ + card->ioport_addr = ioport_map(pdev->resource[0]->start, + resource_size(pdev->resource[0])); + if (!card->ioport_addr) { + err = -ENOMEM; + goto probe_err_3; + } + + /* display firware version */ + dev_info(&pdev->dev, "firmware %d.%d\n", + pcan_read_reg(card, PCC_FW_MAJOR), + pcan_read_reg(card, PCC_FW_MINOR)); + + /* detect available channels */ + pcan_add_channels(card); + if (!card->chan_count) { + ioport_unmap(card->ioport_addr); + goto probe_err_3; + } + + /* init the timer which controls the leds */ + init_timer(&card->led_timer); + card->led_timer.function = pcan_led_timer; + card->led_timer.data = (unsigned long)card; + + /* request the given irq */ + err = request_irq(pdev->irq, &pcan_isr, IRQF_SHARED, PCC_NAME, card); + if (err) + goto probe_err_4; + + /* power on the connectors */ + pcan_set_can_power(card, 1); + + return 0; + +probe_err_4: + /* unregister can devices from network */ + pcan_free_channels(card); + +probe_err_3: + kfree(card); + pdev->priv = NULL; + +probe_err_2: + pcmcia_disable_device(pdev); + +probe_err_1: + return err; +} + +/* + * release claimed resources + */ +static void pcan_remove(struct pcmcia_device *pdev) +{ + pcan_free(pdev); + pcmcia_disable_device(pdev); +} + +static struct pcmcia_driver pcan_driver = { + .name = PCC_NAME, + .probe = pcan_probe, + .remove = pcan_remove, + .id_table = pcan_table, +}; + +static int __init pcan_init(void) +{ + return pcmcia_register_driver(&pcan_driver); +} +module_init(pcan_init); + +static void __exit pcan_exit(void) +{ + pcmcia_unregister_driver(&pcan_driver); +} +module_exit(pcan_exit); -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2012-02-02 16:33 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-01-31 13:19 [PATCH 0/2] can/sja1000: Add/upgrade PEAK-System sja1000 driver Stephane Grosjean 2012-01-31 13:19 ` [PATCH 1/2] can/sja1000: Add PEAK-System PCIe/PCIeC and miniPCI boards Stephane Grosjean 2012-01-31 13:25 ` Marc Kleine-Budde 2012-01-31 15:01 ` Stephane Grosjean 2012-01-31 15:16 ` Wolfgang Grandegger 2012-01-31 15:26 ` Stephane Grosjean 2012-02-02 16:33 ` Stephane Grosjean 2012-01-31 16:55 ` Marc Kleine-Budde 2012-01-31 22:54 ` Marc Kleine-Budde 2012-01-31 13:19 ` [PATCH 2/2] can/sja1000: Add support of PEAK-System PCMCIA board Stephane Grosjean
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).