diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-10-30 12:43:08 +0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-10-30 12:43:08 +0400 |
commit | 169ed55bd30305b933f52bfab32a58671d44ab68 (patch) | |
tree | 32e280957474f458901abfce16fa2a1687ef7497 /drivers/net/can | |
parent | 3d7851b3cdd43a734e5cc4c643fd886ab28ad4d5 (diff) | |
parent | 45f81b1c96d9793e47ce925d257ea693ce0b193e (diff) | |
download | linux-169ed55bd30305b933f52bfab32a58671d44ab68.tar.xz |
Merge branch 'tip/perf/jump-label-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into perf/urgent
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/can/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/at91_can.c | 95 | ||||
-rw-r--r-- | drivers/net/can/flexcan.c | 3 | ||||
-rw-r--r-- | drivers/net/can/mcp251x.c | 106 | ||||
-rw-r--r-- | drivers/net/can/mscan/mpc5xxx_can.c | 8 | ||||
-rw-r--r-- | drivers/net/can/pch_can.c | 1463 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Kconfig | 12 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/sja1000/tscan1.c | 216 |
10 files changed, 1842 insertions, 71 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 9d9e45394433..080574b0fff0 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -82,6 +82,14 @@ config CAN_FLEXCAN ---help--- Say Y here if you want to support for Freescale FlexCAN. +config PCH_CAN + tristate "PCH CAN" + depends on CAN_DEV && PCI + ---help--- + This driver is for PCH CAN of Topcliff which is an IOH for x86 + embedded processor. + This driver can access CAN bus. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 00575373bbd0..90af15a4f106 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -17,5 +17,6 @@ obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o +obj-$(CONFIG_PCH_CAN) += pch_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 2d8bd86bc5e2..cee98fa668bd 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -2,7 +2,7 @@ * at91_can.c - CAN network driver for AT91 SoC CAN controller * * (C) 2007 by Hans J. Koch <hjk@linutronix.de> - * (C) 2008, 2009 by Marc Kleine-Budde <kernel@pengutronix.de> + * (C) 2008, 2009, 2010 by Marc Kleine-Budde <kernel@pengutronix.de> * * This software may be distributed under the terms of the GNU General * Public License ("GPL") version 2 as distributed in the 'COPYING' @@ -40,7 +40,6 @@ #include <mach/board.h> -#define DRV_NAME "at91_can" #define AT91_NAPI_WEIGHT 12 /* @@ -172,6 +171,7 @@ struct at91_priv { }; static struct can_bittiming_const at91_bittiming_const = { + .name = KBUILD_MODNAME, .tseg1_min = 4, .tseg1_max = 16, .tseg2_min = 2, @@ -199,13 +199,13 @@ static inline int get_tx_echo_mb(const struct at91_priv *priv) static inline u32 at91_read(const struct at91_priv *priv, enum at91_reg reg) { - return readl(priv->reg_base + reg); + return __raw_readl(priv->reg_base + reg); } static inline void at91_write(const struct at91_priv *priv, enum at91_reg reg, u32 value) { - writel(value, priv->reg_base + reg); + __raw_writel(value, priv->reg_base + reg); } static inline void set_mb_mode_prio(const struct at91_priv *priv, @@ -243,6 +243,12 @@ static void at91_setup_mailboxes(struct net_device *dev) set_mb_mode(priv, i, AT91_MB_MODE_RX); set_mb_mode(priv, AT91_MB_RX_LAST, AT91_MB_MODE_RX_OVRWR); + /* reset acceptance mask and id register */ + for (i = AT91_MB_RX_FIRST; i <= AT91_MB_RX_LAST; i++) { + at91_write(priv, AT91_MAM(i), 0x0 ); + at91_write(priv, AT91_MID(i), AT91_MID_MIDE); + } + /* The last 4 mailboxes are used for transmitting. */ for (i = AT91_MB_TX_FIRST; i <= AT91_MB_TX_LAST; i++) set_mb_mode_prio(priv, i, AT91_MB_MODE_TX, 0); @@ -257,18 +263,30 @@ static int at91_set_bittiming(struct net_device *dev) const struct can_bittiming *bt = &priv->can.bittiming; u32 reg_br; - reg_br = ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 24) | - ((bt->brp - 1) << 16) | ((bt->sjw - 1) << 12) | + reg_br = ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 << 24 : 0) | + ((bt->brp - 1) << 16) | ((bt->sjw - 1) << 12) | ((bt->prop_seg - 1) << 8) | ((bt->phase_seg1 - 1) << 4) | ((bt->phase_seg2 - 1) << 0); - dev_info(dev->dev.parent, "writing AT91_BR: 0x%08x\n", reg_br); + netdev_info(dev, "writing AT91_BR: 0x%08x\n", reg_br); at91_write(priv, AT91_BR, reg_br); return 0; } +static int at91_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_ecr = at91_read(priv, AT91_ECR); + + bec->rxerr = reg_ecr & 0xff; + bec->txerr = reg_ecr >> 16; + + return 0; +} + static void at91_chip_start(struct net_device *dev) { struct at91_priv *priv = netdev_priv(dev); @@ -281,6 +299,7 @@ static void at91_chip_start(struct net_device *dev) reg_mr = at91_read(priv, AT91_MR); at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); + at91_set_bittiming(dev); at91_setup_mailboxes(dev); at91_transceiver_switch(priv, 1); @@ -350,8 +369,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(!(at91_read(priv, AT91_MSR(mb)) & AT91_MSR_MRDY))) { netif_stop_queue(dev); - dev_err(dev->dev.parent, - "BUG! TX buffer full when queue awake!\n"); + netdev_err(dev, "BUG! TX buffer full when queue awake!\n"); return NETDEV_TX_BUSY; } @@ -435,7 +453,7 @@ static void at91_rx_overflow_err(struct net_device *dev) struct sk_buff *skb; struct can_frame *cf; - dev_dbg(dev->dev.parent, "RX buffer overflow\n"); + netdev_dbg(dev, "RX buffer overflow\n"); stats->rx_over_errors++; stats->rx_errors++; @@ -480,6 +498,9 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb, *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); + /* allow RX of extended frames */ + at91_write(priv, AT91_MID(mb), AT91_MID_MIDE); + if (unlikely(mb == AT91_MB_RX_LAST && reg_msr & AT91_MSR_MMI)) at91_rx_overflow_err(dev); } @@ -565,8 +586,8 @@ static int at91_poll_rx(struct net_device *dev, int quota) if (priv->rx_next > AT91_MB_RX_LOW_LAST && reg_sr & AT91_MB_RX_LOW_MASK) - dev_info(dev->dev.parent, - "order of incoming frames cannot be guaranteed\n"); + netdev_info(dev, + "order of incoming frames cannot be guaranteed\n"); again: for (mb = find_next_bit(addr, AT91_MB_RX_NUM, priv->rx_next); @@ -604,7 +625,7 @@ static void at91_poll_err_frame(struct net_device *dev, /* CRC error */ if (reg_sr & AT91_IRQ_CERR) { - dev_dbg(dev->dev.parent, "CERR irq\n"); + netdev_dbg(dev, "CERR irq\n"); dev->stats.rx_errors++; priv->can.can_stats.bus_error++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; @@ -612,7 +633,7 @@ static void at91_poll_err_frame(struct net_device *dev, /* Stuffing Error */ if (reg_sr & AT91_IRQ_SERR) { - dev_dbg(dev->dev.parent, "SERR irq\n"); + netdev_dbg(dev, "SERR irq\n"); dev->stats.rx_errors++; priv->can.can_stats.bus_error++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; @@ -621,14 +642,14 @@ static void at91_poll_err_frame(struct net_device *dev, /* Acknowledgement Error */ if (reg_sr & AT91_IRQ_AERR) { - dev_dbg(dev->dev.parent, "AERR irq\n"); + netdev_dbg(dev, "AERR irq\n"); dev->stats.tx_errors++; cf->can_id |= CAN_ERR_ACK; } /* Form error */ if (reg_sr & AT91_IRQ_FERR) { - dev_dbg(dev->dev.parent, "FERR irq\n"); + netdev_dbg(dev, "FERR irq\n"); dev->stats.rx_errors++; priv->can.can_stats.bus_error++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; @@ -637,7 +658,7 @@ static void at91_poll_err_frame(struct net_device *dev, /* Bit Error */ if (reg_sr & AT91_IRQ_BERR) { - dev_dbg(dev->dev.parent, "BERR irq\n"); + netdev_dbg(dev, "BERR irq\n"); dev->stats.tx_errors++; priv->can.can_stats.bus_error++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; @@ -755,12 +776,10 @@ static void at91_irq_err_state(struct net_device *dev, struct can_frame *cf, enum can_state new_state) { struct at91_priv *priv = netdev_priv(dev); - u32 reg_idr, reg_ier, reg_ecr; - u8 tec, rec; + u32 reg_idr = 0, reg_ier = 0; + struct can_berr_counter bec; - reg_ecr = at91_read(priv, AT91_ECR); - rec = reg_ecr & 0xff; - tec = reg_ecr >> 16; + at91_get_berr_counter(dev, &bec); switch (priv->can.state) { case CAN_STATE_ERROR_ACTIVE: @@ -771,11 +790,11 @@ static void at91_irq_err_state(struct net_device *dev, */ if (new_state >= CAN_STATE_ERROR_WARNING && new_state <= CAN_STATE_BUS_OFF) { - dev_dbg(dev->dev.parent, "Error Warning IRQ\n"); + netdev_dbg(dev, "Error Warning IRQ\n"); priv->can.can_stats.error_warning++; cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = (tec > rec) ? + cf->data[1] = (bec.txerr > bec.rxerr) ? CAN_ERR_CRTL_TX_WARNING : CAN_ERR_CRTL_RX_WARNING; } @@ -787,11 +806,11 @@ static void at91_irq_err_state(struct net_device *dev, */ if (new_state >= CAN_STATE_ERROR_PASSIVE && new_state <= CAN_STATE_BUS_OFF) { - dev_dbg(dev->dev.parent, "Error Passive IRQ\n"); + netdev_dbg(dev, "Error Passive IRQ\n"); priv->can.can_stats.error_passive++; cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = (tec > rec) ? + cf->data[1] = (bec.txerr > bec.rxerr) ? CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; } @@ -804,7 +823,7 @@ static void at91_irq_err_state(struct net_device *dev, if (new_state <= CAN_STATE_ERROR_PASSIVE) { cf->can_id |= CAN_ERR_RESTARTED; - dev_dbg(dev->dev.parent, "restarted\n"); + netdev_dbg(dev, "restarted\n"); priv->can.can_stats.restarts++; netif_carrier_on(dev); @@ -825,7 +844,7 @@ static void at91_irq_err_state(struct net_device *dev, * circumstances. so just enable AT91_IRQ_ERRP, thus * the "fallthrough" */ - dev_dbg(dev->dev.parent, "Error Active\n"); + netdev_dbg(dev, "Error Active\n"); cf->can_id |= CAN_ERR_PROT; cf->data[2] = CAN_ERR_PROT_ACTIVE; case CAN_STATE_ERROR_WARNING: /* fallthrough */ @@ -843,7 +862,7 @@ static void at91_irq_err_state(struct net_device *dev, cf->can_id |= CAN_ERR_BUSOFF; - dev_dbg(dev->dev.parent, "bus-off\n"); + netdev_dbg(dev, "bus-off\n"); netif_carrier_off(dev); priv->can.can_stats.bus_off++; @@ -881,7 +900,7 @@ static void at91_irq_err(struct net_device *dev) else if (likely(reg_sr & AT91_IRQ_ERRA)) new_state = CAN_STATE_ERROR_ACTIVE; else { - dev_err(dev->dev.parent, "BUG! hardware in undefined state\n"); + netdev_err(dev, "BUG! hardware in undefined state\n"); return; } @@ -1018,7 +1037,7 @@ static const struct net_device_ops at91_netdev_ops = { .ndo_start_xmit = at91_start_xmit, }; -static int __init at91_can_probe(struct platform_device *pdev) +static int __devinit at91_can_probe(struct platform_device *pdev) { struct net_device *dev; struct at91_priv *priv; @@ -1067,8 +1086,8 @@ static int __init at91_can_probe(struct platform_device *pdev) priv = netdev_priv(dev); priv->can.clock.freq = clk_get_rate(clk); priv->can.bittiming_const = &at91_bittiming_const; - priv->can.do_set_bittiming = at91_set_bittiming; priv->can.do_set_mode = at91_set_mode; + priv->can.do_get_berr_counter = at91_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; priv->reg_base = addr; priv->dev = dev; @@ -1092,7 +1111,7 @@ static int __init at91_can_probe(struct platform_device *pdev) return 0; exit_free: - free_netdev(dev); + free_candev(dev); exit_iounmap: iounmap(addr); exit_release: @@ -1113,8 +1132,6 @@ static int __devexit at91_can_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - free_netdev(dev); - iounmap(priv->reg_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1122,6 +1139,8 @@ static int __devexit at91_can_remove(struct platform_device *pdev) clk_put(priv->clk); + free_candev(dev); + return 0; } @@ -1129,21 +1148,19 @@ static struct platform_driver at91_can_driver = { .probe = at91_can_probe, .remove = __devexit_p(at91_can_remove), .driver = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .owner = THIS_MODULE, }, }; static int __init at91_can_module_init(void) { - printk(KERN_INFO "%s netdevice driver\n", DRV_NAME); return platform_driver_register(&at91_can_driver); } static void __exit at91_can_module_exit(void) { platform_driver_unregister(&at91_can_driver); - printk(KERN_INFO "%s: driver removed\n", DRV_NAME); } module_init(at91_can_module_init); @@ -1151,4 +1168,4 @@ module_exit(at91_can_module_exit); MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>"); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver"); +MODULE_DESCRIPTION(KBUILD_MODNAME " CAN netdevice driver"); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index ef443a090ba7..d4990568baee 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -992,7 +992,6 @@ static int __devexit flexcan_remove(struct platform_device *pdev) unregister_flexcandev(dev); platform_set_drvdata(pdev, NULL); - free_candev(dev); iounmap(priv->base); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1000,6 +999,8 @@ static int __devexit flexcan_remove(struct platform_device *pdev) clk_put(priv->clk); + free_candev(dev); + return 0; } diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index b11a0cb5ed81..7ab534aee452 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -38,14 +38,14 @@ * static struct mcp251x_platform_data mcp251x_info = { * .oscillator_frequency = 8000000, * .board_specific_setup = &mcp251x_setup, - * .model = CAN_MCP251X_MCP2510, * .power_enable = mcp251x_power_enable, * .transceiver_enable = NULL, * }; * * static struct spi_board_info spi_board_info[] = { * { - * .modalias = "mcp251x", + * .modalias = "mcp2510", + * // or "mcp2515" depending on your controller * .platform_data = &mcp251x_info, * .irq = IRQ_EINT13, * .max_speed_hz = 2*1000*1000, @@ -125,6 +125,9 @@ # define CANINTF_TX0IF 0x04 # define CANINTF_RX1IF 0x02 # define CANINTF_RX0IF 0x01 +# define CANINTF_RX (CANINTF_RX0IF | CANINTF_RX1IF) +# define CANINTF_TX (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF) +# define CANINTF_ERR (CANINTF_ERRIF) #define EFLG 0x2d # define EFLG_EWARN 0x01 # define EFLG_RXWAR 0x02 @@ -166,6 +169,7 @@ # define RXBSIDH_SHIFT 3 #define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF) # define RXBSIDL_IDE 0x08 +# define RXBSIDL_SRR 0x10 # define RXBSIDL_EID 3 # define RXBSIDL_SHIFT 5 #define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF) @@ -222,10 +226,16 @@ static struct can_bittiming_const mcp251x_bittiming_const = { .brp_inc = 1, }; +enum mcp251x_model { + CAN_MCP251X_MCP2510 = 0x2510, + CAN_MCP251X_MCP2515 = 0x2515, +}; + struct mcp251x_priv { struct can_priv can; struct net_device *net; struct spi_device *spi; + enum mcp251x_model model; struct mutex mcp_lock; /* SPI device lock */ @@ -250,6 +260,16 @@ struct mcp251x_priv { int restart_tx; }; +#define MCP251X_IS(_model) \ +static inline int mcp251x_is_##_model(struct spi_device *spi) \ +{ \ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); \ + return priv->model == CAN_MCP251X_MCP##_model; \ +} + +MCP251X_IS(2510); +MCP251X_IS(2515); + static void mcp251x_clean(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); @@ -319,6 +339,20 @@ static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg) return val; } +static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg, + uint8_t *v1, uint8_t *v2) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + mcp251x_spi_trans(spi, 4); + + *v1 = priv->spi_rx_buf[2]; + *v2 = priv->spi_rx_buf[3]; +} + static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val) { struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); @@ -346,10 +380,9 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg, static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, int len, int tx_buf_idx) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); - if (pdata->model == CAN_MCP251X_MCP2510) { + if (mcp251x_is_2510(spi)) { int i; for (i = 1; i < TXBDAT_OFF + len; i++) @@ -392,9 +425,8 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, int buf_idx) { struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - if (pdata->model == CAN_MCP251X_MCP2510) { + if (mcp251x_is_2510(spi)) { int i, len; for (i = 1; i < RXBDAT_OFF; i++) @@ -444,6 +476,8 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) frame->can_id = (buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); + if (buf[RXBSIDL_OFF] & RXBSIDL_SRR) + frame->can_id |= CAN_RTR_FLAG; } /* Data length */ frame->can_dlc = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); @@ -451,7 +485,7 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) priv->net->stats.rx_packets++; priv->net->stats.rx_bytes += frame->can_dlc; - netif_rx(skb); + netif_rx_ni(skb); } static void mcp251x_hw_sleep(struct spi_device *spi) @@ -674,9 +708,9 @@ static void mcp251x_error_skb(struct net_device *net, int can_id, int data1) skb = alloc_can_err_skb(net, &frame); if (skb) { - frame->can_id = can_id; + frame->can_id |= can_id; frame->data[1] = data1; - netif_rx(skb); + netif_rx_ni(skb); } else { dev_err(&net->dev, "cannot allocate error skb\n"); @@ -754,24 +788,42 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) mutex_lock(&priv->mcp_lock); while (!priv->force_quit) { enum can_state new_state; - u8 intf = mcp251x_read_reg(spi, CANINTF); - u8 eflag; + u8 intf, eflag; + u8 clear_intf = 0; int can_id = 0, data1 = 0; + mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); + + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + + /* receive buffer 0 */ if (intf & CANINTF_RX0IF) { mcp251x_hw_rx(spi, 0); - /* Free one buffer ASAP */ - mcp251x_write_bits(spi, CANINTF, intf & CANINTF_RX0IF, - 0x00); + /* + * Free one buffer ASAP + * (The MCP2515 does this automatically.) + */ + if (mcp251x_is_2510(spi)) + mcp251x_write_bits(spi, CANINTF, CANINTF_RX0IF, 0x00); } - if (intf & CANINTF_RX1IF) + /* receive buffer 1 */ + if (intf & CANINTF_RX1IF) { mcp251x_hw_rx(spi, 1); + /* the MCP2515 does this automatically */ + if (mcp251x_is_2510(spi)) + clear_intf |= CANINTF_RX1IF; + } - mcp251x_write_bits(spi, CANINTF, intf, 0x00); + /* any error or tx interrupt we need to clear? */ + if (intf & (CANINTF_ERR | CANINTF_TX)) + clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); + if (clear_intf) + mcp251x_write_bits(spi, CANINTF, clear_intf, 0x00); - eflag = mcp251x_read_reg(spi, EFLG); - mcp251x_write_reg(spi, EFLG, 0x00); + if (eflag) + mcp251x_write_bits(spi, EFLG, eflag, 0x00); /* Update can state */ if (eflag & EFLG_TXBO) { @@ -816,10 +868,14 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (intf & CANINTF_ERRIF) { /* Handle overflow counters */ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) { - if (eflag & EFLG_RX0OVR) + if (eflag & EFLG_RX0OVR) { net->stats.rx_over_errors++; - if (eflag & EFLG_RX1OVR) + net->stats.rx_errors++; + } + if (eflag & EFLG_RX1OVR) { net->stats.rx_over_errors++; + net->stats.rx_errors++; + } can_id |= CAN_ERR_CRTL; data1 |= CAN_ERR_CRTL_RX_OVERFLOW; } @@ -838,7 +894,7 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (intf == 0) break; - if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) { + if (intf & CANINTF_TX) { net->stats.tx_packets++; net->stats.tx_bytes += priv->tx_len - 1; if (priv->tx_len) { @@ -921,16 +977,12 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) struct net_device *net; struct mcp251x_priv *priv; struct mcp251x_platform_data *pdata = spi->dev.platform_data; - int model = spi_get_device_id(spi)->driver_data; int ret = -ENODEV; if (!pdata) /* Platform data is required for osc freq */ goto error_out; - if (model) - pdata->model = model; - /* Allocate can/net device */ net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); if (!net) { @@ -947,6 +999,7 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) priv->can.clock.freq = pdata->oscillator_frequency / 2; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; + priv->model = spi_get_device_id(spi)->driver_data; priv->net = net; dev_set_drvdata(&spi->dev, priv); @@ -1120,8 +1173,7 @@ static int mcp251x_can_resume(struct spi_device *spi) #define mcp251x_can_resume NULL #endif -static struct spi_device_id mcp251x_id_table[] = { - { "mcp251x", 0 /* Use pdata.model */ }, +static const struct spi_device_id mcp251x_id_table[] = { { "mcp2510", CAN_MCP251X_MCP2510 }, { "mcp2515", CAN_MCP251X_MCP2515 }, { }, diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index b1bdc909090f..312b9c8f4f3b 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -143,12 +143,12 @@ static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, np_clock = of_find_matching_node(NULL, mpc512x_clock_ids); if (!np_clock) { dev_err(&ofdev->dev, "couldn't find clock node\n"); - return -ENODEV; + return 0; } clockctl = of_iomap(np_clock, 0); if (!clockctl) { dev_err(&ofdev->dev, "couldn't map clock registers\n"); - return 0; + goto exit_put; } /* Determine the MSCAN device index from the physical address */ @@ -233,9 +233,9 @@ static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, clocksrc == 1 ? "ref_clk" : "sys_clk", clockdiv); exit_unmap: - of_node_put(np_clock); iounmap(clockctl); - +exit_put: + of_node_put(np_clock); return freq; } #else /* !CONFIG_PPC_MPC512x */ diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c new file mode 100644 index 000000000000..55ec324caaf4 --- /dev/null +++ b/drivers/net/can/pch_can.c @@ -0,0 +1,1463 @@ +/* + * Copyright (C) 1999 - 2010 Intel Corporation. + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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/interrupt.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#define MAX_MSG_OBJ 32 +#define MSG_OBJ_RX 0 /* The receive message object flag. */ +#define MSG_OBJ_TX 1 /* The transmit message object flag. */ + +#define ENABLE 1 /* The enable flag */ +#define DISABLE 0 /* The disable flag */ +#define CAN_CTRL_INIT 0x0001 /* The INIT bit of CANCONT register. */ +#define CAN_CTRL_IE 0x0002 /* The IE bit of CAN control register */ +#define CAN_CTRL_IE_SIE_EIE 0x000e +#define CAN_CTRL_CCE 0x0040 +#define CAN_CTRL_OPT 0x0080 /* The OPT bit of CANCONT register. */ +#define CAN_OPT_SILENT 0x0008 /* The Silent bit of CANOPT reg. */ +#define CAN_OPT_LBACK 0x0010 /* The LoopBack bit of CANOPT reg. */ +#define CAN_CMASK_RX_TX_SET 0x00f3 +#define CAN_CMASK_RX_TX_GET 0x0073 +#define CAN_CMASK_ALL 0xff +#define CAN_CMASK_RDWR 0x80 +#define CAN_CMASK_ARB 0x20 +#define CAN_CMASK_CTRL 0x10 +#define CAN_CMASK_MASK 0x40 +#define CAN_CMASK_NEWDAT 0x04 +#define CAN_CMASK_CLRINTPND 0x08 + +#define CAN_IF_MCONT_NEWDAT 0x8000 +#define CAN_IF_MCONT_INTPND 0x2000 +#define CAN_IF_MCONT_UMASK 0x1000 +#define CAN_IF_MCONT_TXIE 0x0800 +#define CAN_IF_MCONT_RXIE 0x0400 +#define CAN_IF_MCONT_RMTEN 0x0200 +#define CAN_IF_MCONT_TXRQXT 0x0100 +#define CAN_IF_MCONT_EOB 0x0080 +#define CAN_IF_MCONT_DLC 0x000f +#define CAN_IF_MCONT_MSGLOST 0x4000 +#define CAN_MASK2_MDIR_MXTD 0xc000 +#define CAN_ID2_DIR 0x2000 +#define CAN_ID_MSGVAL 0x8000 + +#define CAN_STATUS_INT 0x8000 +#define CAN_IF_CREQ_BUSY 0x8000 +#define CAN_ID2_XTD 0x4000 + +#define CAN_REC 0x00007f00 +#define CAN_TEC 0x000000ff + +#define PCH_RX_OK 0x00000010 +#define PCH_TX_OK 0x00000008 +#define PCH_BUS_OFF 0x00000080 +#define PCH_EWARN 0x00000040 +#define PCH_EPASSIV 0x00000020 +#define PCH_LEC0 0x00000001 +#define PCH_LEC1 0x00000002 +#define PCH_LEC2 0x00000004 +#define PCH_LEC_ALL (PCH_LEC0 | PCH_LEC1 | PCH_LEC2) +#define PCH_STUF_ERR PCH_LEC0 +#define PCH_FORM_ERR PCH_LEC1 +#define PCH_ACK_ERR (PCH_LEC0 | PCH_LEC1) +#define PCH_BIT1_ERR PCH_LEC2 +#define PCH_BIT0_ERR (PCH_LEC0 | PCH_LEC2) +#define PCH_CRC_ERR (PCH_LEC1 | PCH_LEC2) + +/* bit position of certain controller bits. */ +#define BIT_BITT_BRP 0 +#define BIT_BITT_SJW 6 +#define BIT_BITT_TSEG1 8 +#define BIT_BITT_TSEG2 12 +#define BIT_IF1_MCONT_RXIE 10 +#define BIT_IF2_MCONT_TXIE 11 +#define BIT_BRPE_BRPE 6 +#define BIT_ES_TXERRCNT 0 +#define BIT_ES_RXERRCNT 8 +#define MSK_BITT_BRP 0x3f +#define MSK_BITT_SJW 0xc0 +#define MSK_BITT_TSEG1 0xf00 +#define MSK_BITT_TSEG2 0x7000 +#define MSK_BRPE_BRPE 0x3c0 +#define MSK_BRPE_GET 0x0f +#define MSK_CTRL_IE_SIE_EIE 0x07 +#define MSK_MCONT_TXIE 0x08 +#define MSK_MCONT_RXIE 0x10 +#define PCH_CAN_NO_TX_BUFF 1 +#define COUNTER_LIMIT 10 + +#define PCH_CAN_CLK 50000000 /* 50MHz */ + +/* Define the number of message object. + * PCH CAN communications are done via Message RAM. + * The Message RAM consists of 32 message objects. */ +#define PCH_RX_OBJ_NUM 26 /* 1~ PCH_RX_OBJ_NUM is Rx*/ +#define PCH_TX_OBJ_NUM 6 /* PCH_RX_OBJ_NUM is RX ~ Tx*/ +#define PCH_OBJ_NUM (PCH_TX_OBJ_NUM + PCH_RX_OBJ_NUM) + +#define PCH_FIFO_THRESH 16 + +enum pch_can_mode { + PCH_CAN_ENABLE, + PCH_CAN_DISABLE, + PCH_CAN_ALL, + PCH_CAN_NONE, + PCH_CAN_STOP, + PCH_CAN_RUN +}; + +struct pch_can_regs { + u32 cont; + u32 stat; + u32 errc; + u32 bitt; + u32 intr; + u32 opt; + u32 brpe; + u32 reserve1; + u32 if1_creq; + u32 if1_cmask; + u32 if1_mask1; + u32 if1_mask2; + u32 if1_id1; + u32 if1_id2; + u32 if1_mcont; + u32 if1_dataa1; + u32 if1_dataa2; + u32 if1_datab1; + u32 if1_datab2; + u32 reserve2; + u32 reserve3[12]; + u32 if2_creq; + u32 if2_cmask; + u32 if2_mask1; + u32 if2_mask2; + u32 if2_id1; + u32 if2_id2; + u32 if2_mcont; + u32 if2_dataa1; + u32 if2_dataa2; + u32 if2_datab1; + u32 if2_datab2; + u32 reserve4; + u32 reserve5[20]; + u32 treq1; + u32 treq2; + u32 reserve6[2]; + u32 reserve7[56]; + u32 reserve8[3]; + u32 srst; +}; + +struct pch_can_priv { + struct can_priv can; + unsigned int can_num; + struct pci_dev *dev; + unsigned int tx_enable[MAX_MSG_OBJ]; + unsigned int rx_enable[MAX_MSG_OBJ]; + unsigned int rx_link[MAX_MSG_OBJ]; + unsigned int int_enables; + unsigned int int_stat; + struct net_device *ndev; + spinlock_t msgif_reg_lock; /* Message Interface Registers Access Lock*/ + unsigned int msg_obj[MAX_MSG_OBJ]; + struct pch_can_regs __iomem *regs; + struct napi_struct napi; + unsigned int tx_obj; /* Point next Tx Obj index */ + unsigned int use_msi; +}; + +static struct can_bittiming_const pch_can_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, /* 6bit + extended 4bit */ + .brp_inc = 1, +}; + +static DEFINE_PCI_DEVICE_TABLE(pch_pci_tbl) = { + {PCI_VENDOR_ID_INTEL, 0x8818, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, pch_pci_tbl); + +static inline void pch_can_bit_set(u32 *addr, u32 mask) +{ + iowrite32(ioread32(addr) | mask, addr); +} + +static inline void pch_can_bit_clear(u32 *addr, u32 mask) +{ + iowrite32(ioread32(addr) & ~mask, addr); +} + +static void pch_can_set_run_mode(struct pch_can_priv *priv, + enum pch_can_mode mode) +{ + switch (mode) { + case PCH_CAN_RUN: + pch_can_bit_clear(&priv->regs->cont, CAN_CTRL_INIT); + break; + + case PCH_CAN_STOP: + pch_can_bit_set(&priv->regs->cont, CAN_CTRL_INIT); + break; + + default: + dev_err(&priv->ndev->dev, "%s -> Invalid Mode.\n", __func__); + break; + } +} + +static void pch_can_set_optmode(struct pch_can_priv *priv) +{ + u32 reg_val = ioread32(&priv->regs->opt); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg_val |= CAN_OPT_SILENT; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg_val |= CAN_OPT_LBACK; + + pch_can_bit_set(&priv->regs->cont, CAN_CTRL_OPT); + iowrite32(reg_val, &priv->regs->opt); +} + +static void pch_can_set_int_custom(struct pch_can_priv *priv) +{ + /* Clearing the IE, SIE and EIE bits of Can control register. */ + pch_can_bit_clear(&priv->regs->cont, CAN_CTRL_IE_SIE_EIE); + + /* Appropriately setting them. */ + pch_can_bit_set(&priv->regs->cont, + ((priv->int_enables & MSK_CTRL_IE_SIE_EIE) << 1)); +} + +/* This function retrieves interrupt enabled for the CAN device. */ +static void pch_can_get_int_enables(struct pch_can_priv *priv, u32 *enables) +{ + /* Obtaining the status of IE, SIE and EIE interrupt bits. */ + *enables = ((ioread32(&priv->regs->cont) & CAN_CTRL_IE_SIE_EIE) >> 1); +} + +static void pch_can_set_int_enables(struct pch_can_priv *priv, + enum pch_can_mode interrupt_no) +{ + switch (interrupt_no) { + case PCH_CAN_ENABLE: + pch_can_bit_set(&priv->regs->cont, CAN_CTRL_IE); + break; + + case PCH_CAN_DISABLE: + pch_can_bit_clear(&priv->regs->cont, CAN_CTRL_IE); + break; + + case PCH_CAN_ALL: + pch_can_bit_set(&priv->regs->cont, CAN_CTRL_IE_SIE_EIE); + break; + + case PCH_CAN_NONE: + pch_can_bit_clear(&priv->regs->cont, CAN_CTRL_IE_SIE_EIE); + break; + + default: + dev_err(&priv->ndev->dev, "Invalid interrupt number.\n"); + break; + } +} + +static void pch_can_check_if_busy(u32 __iomem *creq_addr, u32 num) +{ + u32 counter = COUNTER_LIMIT; + u32 ifx_creq; + + iowrite32(num, creq_addr); + while (counter) { + ifx_creq = ioread32(creq_addr) & CAN_IF_CREQ_BUSY; + if (!ifx_creq) + break; + counter--; + udelay(1); + } + if (!counter) + pr_err("%s:IF1 BUSY Flag is set forever.\n", __func__); +} + +static void pch_can_set_rx_enable(struct pch_can_priv *priv, u32 buff_num, + u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + /* Reading the receive buffer data from RAM to Interface1 registers */ + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, buff_num); + + /* Setting the IF1MASK1 register to access MsgVal and RxIE bits */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if1_cmask); + + if (set == ENABLE) { + /* Setting the MsgVal and RxIE bits */ + pch_can_bit_set(&priv->regs->if1_mcont, CAN_IF_MCONT_RXIE); + pch_can_bit_set(&priv->regs->if1_id2, CAN_ID_MSGVAL); + + } else if (set == DISABLE) { + /* Resetting the MsgVal and RxIE bits */ + pch_can_bit_clear(&priv->regs->if1_mcont, CAN_IF_MCONT_RXIE); + pch_can_bit_clear(&priv->regs->if1_id2, CAN_ID_MSGVAL); + } + + pch_can_check_if_busy(&priv->regs->if1_creq, buff_num); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_rx_enable_all(struct pch_can_priv *priv) +{ + int i; + + /* Traversing to obtain the object configured as receivers. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_RX) + pch_can_set_rx_enable(priv, i + 1, ENABLE); + } +} + +static void pch_can_rx_disable_all(struct pch_can_priv *priv) +{ + int i; + + /* Traversing to obtain the object configured as receivers. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_RX) + pch_can_set_rx_enable(priv, i + 1, DISABLE); + } +} + +static void pch_can_set_tx_enable(struct pch_can_priv *priv, u32 buff_num, + u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + /* Reading the Msg buffer from Message RAM to Interface2 registers. */ + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, buff_num); + + /* Setting the IF2CMASK register for accessing the + MsgVal and TxIE bits */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if2_cmask); + + if (set == ENABLE) { + /* Setting the MsgVal and TxIE bits */ + pch_can_bit_set(&priv->regs->if2_mcont, CAN_IF_MCONT_TXIE); + pch_can_bit_set(&priv->regs->if2_id2, CAN_ID_MSGVAL); + } else if (set == DISABLE) { + /* Resetting the MsgVal and TxIE bits. */ + pch_can_bit_clear(&priv->regs->if2_mcont, CAN_IF_MCONT_TXIE); + pch_can_bit_clear(&priv->regs->if2_id2, CAN_ID_MSGVAL); + } + + pch_can_check_if_busy(&priv->regs->if2_creq, buff_num); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_tx_enable_all(struct pch_can_priv *priv) +{ + int i; + + /* Traversing to obtain the object configured as transmit object. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_TX) + pch_can_set_tx_enable(priv, i + 1, ENABLE); + } +} + +static void pch_can_tx_disable_all(struct pch_can_priv *priv) +{ + int i; + + /* Traversing to obtain the object configured as transmit object. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_TX) + pch_can_set_tx_enable(priv, i + 1, DISABLE); + } +} + +static void pch_can_get_rx_enable(struct pch_can_priv *priv, u32 buff_num, + u32 *enable) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, buff_num); + + if (((ioread32(&priv->regs->if1_id2)) & CAN_ID_MSGVAL) && + ((ioread32(&priv->regs->if1_mcont)) & + CAN_IF_MCONT_RXIE)) + *enable = ENABLE; + else + *enable = DISABLE; + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_get_tx_enable(struct pch_can_priv *priv, u32 buff_num, + u32 *enable) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, buff_num); + + if (((ioread32(&priv->regs->if2_id2)) & CAN_ID_MSGVAL) && + ((ioread32(&priv->regs->if2_mcont)) & + CAN_IF_MCONT_TXIE)) { + *enable = ENABLE; + } else { + *enable = DISABLE; + } + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static int pch_can_int_pending(struct pch_can_priv *priv) +{ + return ioread32(&priv->regs->intr) & 0xffff; +} + +static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv, + u32 buffer_num, u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, buffer_num); + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_CTRL, &priv->regs->if1_cmask); + if (set == ENABLE) + pch_can_bit_clear(&priv->regs->if1_mcont, CAN_IF_MCONT_EOB); + else + pch_can_bit_set(&priv->regs->if1_mcont, CAN_IF_MCONT_EOB); + + pch_can_check_if_busy(&priv->regs->if1_creq, buffer_num); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_get_rx_buffer_link(struct pch_can_priv *priv, + u32 buffer_num, u32 *link) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, buffer_num); + + if (ioread32(&priv->regs->if1_mcont) & CAN_IF_MCONT_EOB) + *link = DISABLE; + else + *link = ENABLE; + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_clear_buffers(struct pch_can_priv *priv) +{ + int i; + + for (i = 0; i < PCH_RX_OBJ_NUM; i++) { + iowrite32(CAN_CMASK_RX_TX_SET, &priv->regs->if1_cmask); + iowrite32(0xffff, &priv->regs->if1_mask1); + iowrite32(0xffff, &priv->regs->if1_mask2); + iowrite32(0x0, &priv->regs->if1_id1); + iowrite32(0x0, &priv->regs->if1_id2); + iowrite32(0x0, &priv->regs->if1_mcont); + iowrite32(0x0, &priv->regs->if1_dataa1); + iowrite32(0x0, &priv->regs->if1_dataa2); + iowrite32(0x0, &priv->regs->if1_datab1); + iowrite32(0x0, &priv->regs->if1_datab2); + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_MASK | + CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, i+1); + } + + for (i = i; i < PCH_OBJ_NUM; i++) { + iowrite32(CAN_CMASK_RX_TX_SET, &priv->regs->if2_cmask); + iowrite32(0xffff, &priv->regs->if2_mask1); + iowrite32(0xffff, &priv->regs->if2_mask2); + iowrite32(0x0, &priv->regs->if2_id1); + iowrite32(0x0, &priv->regs->if2_id2); + iowrite32(0x0, &priv->regs->if2_mcont); + iowrite32(0x0, &priv->regs->if2_dataa1); + iowrite32(0x0, &priv->regs->if2_dataa2); + iowrite32(0x0, &priv->regs->if2_datab1); + iowrite32(0x0, &priv->regs->if2_datab2); + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_MASK | + CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, i+1); + } +} + +static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_RX) { + iowrite32(CAN_CMASK_RX_TX_GET, + &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, i+1); + + iowrite32(0x0, &priv->regs->if1_id1); + iowrite32(0x0, &priv->regs->if1_id2); + + pch_can_bit_set(&priv->regs->if1_mcont, + CAN_IF_MCONT_UMASK); + + /* Set FIFO mode set to 0 except last Rx Obj*/ + pch_can_bit_clear(&priv->regs->if1_mcont, + CAN_IF_MCONT_EOB); + /* In case FIFO mode, Last EoB of Rx Obj must be 1 */ + if (i == (PCH_RX_OBJ_NUM - 1)) + pch_can_bit_set(&priv->regs->if1_mcont, + CAN_IF_MCONT_EOB); + + iowrite32(0, &priv->regs->if1_mask1); + pch_can_bit_clear(&priv->regs->if1_mask2, + 0x1fff | CAN_MASK2_MDIR_MXTD); + + /* Setting CMASK for writing */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_MASK | + CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if1_cmask); + + pch_can_check_if_busy(&priv->regs->if1_creq, i+1); + } else if (priv->msg_obj[i] == MSG_OBJ_TX) { + iowrite32(CAN_CMASK_RX_TX_GET, + &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, i+1); + + /* Resetting DIR bit for reception */ + iowrite32(0x0, &priv->regs->if2_id1); + iowrite32(0x0, &priv->regs->if2_id2); + pch_can_bit_set(&priv->regs->if2_id2, CAN_ID2_DIR); + + /* Setting EOB bit for transmitter */ + iowrite32(CAN_IF_MCONT_EOB, &priv->regs->if2_mcont); + + pch_can_bit_set(&priv->regs->if2_mcont, + CAN_IF_MCONT_UMASK); + + iowrite32(0, &priv->regs->if2_mask1); + pch_can_bit_clear(&priv->regs->if2_mask2, 0x1fff); + + /* Setting CMASK for writing */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_MASK | + CAN_CMASK_ARB | CAN_CMASK_CTRL, + &priv->regs->if2_cmask); + + pch_can_check_if_busy(&priv->regs->if2_creq, i+1); + } + } + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); +} + +static void pch_can_init(struct pch_can_priv *priv) +{ + /* Stopping the Can device. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Clearing all the message object buffers. */ + pch_can_clear_buffers(priv); + + /* Configuring the respective message object as either rx/tx object. */ + pch_can_config_rx_tx_buffers(priv); + + /* Enabling the interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_ALL); +} + +static void pch_can_release(struct pch_can_priv *priv) +{ + /* Stooping the CAN device. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Disabling the interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_NONE); + + /* Disabling all the receive object. */ + pch_can_rx_disable_all(priv); + + /* Disabling all the transmit object. */ + pch_can_tx_disable_all(priv); +} + +/* This function clears interrupt(s) from the CAN device. */ +static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) +{ + if (mask == CAN_STATUS_INT) { + ioread32(&priv->regs->stat); + return; + } + + /* Clear interrupt for transmit object */ + if (priv->msg_obj[mask - 1] == MSG_OBJ_TX) { + /* Setting CMASK for clearing interrupts for + frame transmission. */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_CTRL | CAN_CMASK_ARB, + &priv->regs->if2_cmask); + + /* Resetting the ID registers. */ + pch_can_bit_set(&priv->regs->if2_id2, + CAN_ID2_DIR | (0x7ff << 2)); + iowrite32(0x0, &priv->regs->if2_id1); + + /* Claring NewDat, TxRqst & IntPnd */ + pch_can_bit_clear(&priv->regs->if2_mcont, + CAN_IF_MCONT_NEWDAT | CAN_IF_MCONT_INTPND | + CAN_IF_MCONT_TXRQXT); + pch_can_check_if_busy(&priv->regs->if2_creq, mask); + } else if (priv->msg_obj[mask - 1] == MSG_OBJ_RX) { + /* Setting CMASK for clearing the reception interrupts. */ + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_CTRL | CAN_CMASK_ARB, + &priv->regs->if1_cmask); + + /* Clearing the Dir bit. */ + pch_can_bit_clear(&priv->regs->if1_id2, CAN_ID2_DIR); + + /* Clearing NewDat & IntPnd */ + pch_can_bit_clear(&priv->regs->if1_mcont, + CAN_IF_MCONT_NEWDAT | CAN_IF_MCONT_INTPND); + + pch_can_check_if_busy(&priv->regs->if1_creq, mask); + } +} + +static int pch_can_get_buffer_status(struct pch_can_priv *priv) +{ + return (ioread32(&priv->regs->treq1) & 0xffff) | + ((ioread32(&priv->regs->treq2) & 0xffff) << 16); +} + +static void pch_can_reset(struct pch_can_priv *priv) +{ + /* write to sw reset register */ + iowrite32(1, &priv->regs->srst); + iowrite32(0, &priv->regs->srst); +} + +static void pch_can_error(struct net_device *ndev, u32 status) +{ + struct sk_buff *skb; + struct pch_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf; + u32 errc; + struct net_device_stats *stats = &(priv->ndev->stats); + enum can_state state = priv->can.state; + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) + return; + + if (status & PCH_BUS_OFF) { + pch_can_tx_disable_all(priv); + pch_can_rx_disable_all(priv); + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(ndev); + pch_can_set_run_mode(priv, PCH_CAN_RUN); + dev_err(&ndev->dev, "%s -> Bus Off occurres.\n", __func__); + } + + /* Warning interrupt. */ + if (status & PCH_EWARN) { + state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + cf->can_id |= CAN_ERR_CRTL; + errc = ioread32(&priv->regs->errc); + if (((errc & CAN_REC) >> 8) > 96) + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + if ((errc & CAN_TEC) > 96) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + dev_warn(&ndev->dev, + "%s -> Error Counter is more than 96.\n", __func__); + } + /* Error passive interrupt. */ + if (status & PCH_EPASSIV) { + priv->can.can_stats.error_passive++; + state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + errc = ioread32(&priv->regs->errc); + if (((errc & CAN_REC) >> 8) > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if ((errc & CAN_TEC) > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + dev_err(&ndev->dev, + "%s -> CAN controller is ERROR PASSIVE .\n", __func__); + } + + if (status & PCH_LEC_ALL) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + switch (status & PCH_LEC_ALL) { + case PCH_STUF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case PCH_FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case PCH_ACK_ERR: + cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL; + break; + case PCH_BIT1_ERR: + case PCH_BIT0_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case PCH_CRC_ERR: + cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + break; + default: + iowrite32(status | PCH_LEC_ALL, &priv->regs->stat); + break; + } + + } + + priv->can.state = state; + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static irqreturn_t pch_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct pch_can_priv *priv = netdev_priv(ndev); + + pch_can_set_int_enables(priv, PCH_CAN_NONE); + + napi_schedule(&priv->napi); + + return IRQ_HANDLED; +} + +static int pch_can_rx_normal(struct net_device *ndev, u32 int_stat) +{ + u32 reg; + canid_t id; + u32 ide; + u32 rtr; + int i, j, k; + int rcv_pkts = 0; + struct sk_buff *skb; + struct can_frame *cf; + struct pch_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &(priv->ndev->stats); + + /* Reading the messsage object from the Message RAM */ + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, int_stat); + + /* Reading the MCONT register. */ + reg = ioread32(&priv->regs->if1_mcont); + reg &= 0xffff; + + for (k = int_stat; !(reg & CAN_IF_MCONT_EOB); k++) { + /* If MsgLost bit set. */ + if (reg & CAN_IF_MCONT_MSGLOST) { + dev_err(&priv->ndev->dev, "Msg Obj is overwritten.\n"); + pch_can_bit_clear(&priv->regs->if1_mcont, + CAN_IF_MCONT_MSGLOST); + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_CTRL, + &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, k); + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) + return -ENOMEM; + + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + netif_receive_skb(skb); + rcv_pkts++; + goto RX_NEXT; + } + if (!(reg & CAN_IF_MCONT_NEWDAT)) + goto RX_NEXT; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) + return -ENOMEM; + + /* Get Received data */ + ide = ((ioread32(&priv->regs->if1_id2)) & CAN_ID2_XTD) >> 14; + if (ide) { + id = (ioread32(&priv->regs->if1_id1) & 0xffff); + id |= (((ioread32(&priv->regs->if1_id2)) & + 0x1fff) << 16); + cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; + } else { + id = (((ioread32(&priv->regs->if1_id2)) & + (CAN_SFF_MASK << 2)) >> 2); + cf->can_id = (id & CAN_SFF_MASK); + } + + rtr = (ioread32(&priv->regs->if1_id2) & CAN_ID2_DIR); + if (rtr) { + cf->can_dlc = 0; + cf->can_id |= CAN_RTR_FLAG; + } else { + cf->can_dlc = ((ioread32(&priv->regs->if1_mcont)) & + 0x0f); + } + + for (i = 0, j = 0; i < cf->can_dlc; j++) { + reg = ioread32(&priv->regs->if1_dataa1 + j*4); + cf->data[i++] = cpu_to_le32(reg & 0xff); + if (i == cf->can_dlc) + break; + cf->data[i++] = cpu_to_le32((reg >> 8) & 0xff); + } + + netif_receive_skb(skb); + rcv_pkts++; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + if (k < PCH_FIFO_THRESH) { + iowrite32(CAN_CMASK_RDWR | CAN_CMASK_CTRL | + CAN_CMASK_ARB, &priv->regs->if1_cmask); + + /* Clearing the Dir bit. */ + pch_can_bit_clear(&priv->regs->if1_id2, CAN_ID2_DIR); + + /* Clearing NewDat & IntPnd */ + pch_can_bit_clear(&priv->regs->if1_mcont, + CAN_IF_MCONT_INTPND); + pch_can_check_if_busy(&priv->regs->if1_creq, k); + } else if (k > PCH_FIFO_THRESH) { + pch_can_int_clr(priv, k); + } else if (k == PCH_FIFO_THRESH) { + int cnt; + for (cnt = 0; cnt < PCH_FIFO_THRESH; cnt++) + pch_can_int_clr(priv, cnt+1); + } +RX_NEXT: + /* Reading the messsage object from the Message RAM */ + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if1_cmask); + pch_can_check_if_busy(&priv->regs->if1_creq, k + 1); + reg = ioread32(&priv->regs->if1_mcont); + } + + return rcv_pkts; +} +static int pch_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct pch_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &(priv->ndev->stats); + u32 dlc; + u32 int_stat; + int rcv_pkts = 0; + u32 reg_stat; + unsigned long flags; + + int_stat = pch_can_int_pending(priv); + if (!int_stat) + return 0; + +INT_STAT: + if (int_stat == CAN_STATUS_INT) { + reg_stat = ioread32(&priv->regs->stat); + if (reg_stat & (PCH_BUS_OFF | PCH_LEC_ALL)) { + if ((reg_stat & PCH_LEC_ALL) != PCH_LEC_ALL) + pch_can_error(ndev, reg_stat); + } + + if (reg_stat & PCH_TX_OK) { + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, + ioread32(&priv->regs->intr)); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); + pch_can_bit_clear(&priv->regs->stat, PCH_TX_OK); + } + + if (reg_stat & PCH_RX_OK) + pch_can_bit_clear(&priv->regs->stat, PCH_RX_OK); + + int_stat = pch_can_int_pending(priv); + if (int_stat == CAN_STATUS_INT) + goto INT_STAT; + } + +MSG_OBJ: + if ((int_stat >= 1) && (int_stat <= PCH_RX_OBJ_NUM)) { + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + rcv_pkts = pch_can_rx_normal(ndev, int_stat); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); + if (rcv_pkts < 0) + return 0; + } else if ((int_stat > PCH_RX_OBJ_NUM) && (int_stat <= PCH_OBJ_NUM)) { + if (priv->msg_obj[int_stat - 1] == MSG_OBJ_TX) { + /* Handle transmission interrupt */ + can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_NUM - 1); + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + iowrite32(CAN_CMASK_RX_TX_GET | CAN_CMASK_CLRINTPND, + &priv->regs->if2_cmask); + dlc = ioread32(&priv->regs->if2_mcont) & + CAN_IF_MCONT_DLC; + pch_can_check_if_busy(&priv->regs->if2_creq, int_stat); + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); + if (dlc > 8) + dlc = 8; + stats->tx_bytes += dlc; + stats->tx_packets++; + } + } + + int_stat = pch_can_int_pending(priv); + if (int_stat == CAN_STATUS_INT) + goto INT_STAT; + else if (int_stat >= 1 && int_stat <= 32) + goto MSG_OBJ; + + napi_complete(napi); + pch_can_set_int_enables(priv, PCH_CAN_ALL); + + return rcv_pkts; +} + +static int pch_set_bittiming(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + u32 canbit; + u32 bepe; + u32 brp; + + /* Setting the CCE bit for accessing the Can Timing register. */ + pch_can_bit_set(&priv->regs->cont, CAN_CTRL_CCE); + + brp = (bt->tq) / (1000000000/PCH_CAN_CLK) - 1; + canbit = brp & MSK_BITT_BRP; + canbit |= (bt->sjw - 1) << BIT_BITT_SJW; + canbit |= (bt->phase_seg1 + bt->prop_seg - 1) << BIT_BITT_TSEG1; + canbit |= (bt->phase_seg2 - 1) << BIT_BITT_TSEG2; + bepe = (brp & MSK_BRPE_BRPE) >> BIT_BRPE_BRPE; + iowrite32(canbit, &priv->regs->bitt); + iowrite32(bepe, &priv->regs->brpe); + pch_can_bit_clear(&priv->regs->cont, CAN_CTRL_CCE); + + return 0; +} + +static void pch_can_start(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + + if (priv->can.state != CAN_STATE_STOPPED) + pch_can_reset(priv); + + pch_set_bittiming(ndev); + pch_can_set_optmode(priv); + + pch_can_tx_enable_all(priv); + pch_can_rx_enable_all(priv); + + /* Setting the CAN to run mode. */ + pch_can_set_run_mode(priv, PCH_CAN_RUN); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return; +} + +static int pch_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + pch_can_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int pch_can_open(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + int retval; + + retval = pci_enable_msi(priv->dev); + if (retval) { + dev_info(&ndev->dev, "PCH CAN opened without MSI\n"); + priv->use_msi = 0; + } else { + dev_info(&ndev->dev, "PCH CAN opened with MSI\n"); + priv->use_msi = 1; + } + + /* Regsitering the interrupt. */ + retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED, + ndev->name, ndev); + if (retval) { + dev_err(&ndev->dev, "request_irq failed.\n"); + goto req_irq_err; + } + + /* Open common can device */ + retval = open_candev(ndev); + if (retval) { + dev_err(ndev->dev.parent, "open_candev() failed %d\n", retval); + goto err_open_candev; + } + + pch_can_init(priv); + pch_can_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_open_candev: + free_irq(priv->dev->irq, ndev); +req_irq_err: + if (priv->use_msi) + pci_disable_msi(priv->dev); + + pch_can_release(priv); + + return retval; +} + +static int pch_close(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + pch_can_release(priv); + free_irq(priv->dev->irq, ndev); + if (priv->use_msi) + pci_disable_msi(priv->dev); + close_candev(ndev); + priv->can.state = CAN_STATE_STOPPED; + return 0; +} + +static int pch_get_msg_obj_sts(struct net_device *ndev, u32 obj_id) +{ + u32 buffer_status = 0; + struct pch_can_priv *priv = netdev_priv(ndev); + + /* Getting the message object status. */ + buffer_status = (u32) pch_can_get_buffer_status(priv); + + return buffer_status & obj_id; +} + + +static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int i, j; + unsigned long flags; + struct pch_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int tx_buffer_avail = 0; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (priv->tx_obj == (PCH_OBJ_NUM + 1)) { /* Point tail Obj */ + while (pch_get_msg_obj_sts(ndev, (((1 << PCH_TX_OBJ_NUM)-1) << + PCH_RX_OBJ_NUM))) + udelay(500); + + priv->tx_obj = PCH_RX_OBJ_NUM + 1; /* Point head of Tx Obj ID */ + tx_buffer_avail = priv->tx_obj; /* Point Tail of Tx Obj */ + } else { + tx_buffer_avail = priv->tx_obj; + } + priv->tx_obj++; + + /* Attaining the lock. */ + spin_lock_irqsave(&priv->msgif_reg_lock, flags); + + /* Reading the Msg Obj from the Msg RAM to the Interface register. */ + iowrite32(CAN_CMASK_RX_TX_GET, &priv->regs->if2_cmask); + pch_can_check_if_busy(&priv->regs->if2_creq, tx_buffer_avail); + + /* Setting the CMASK register. */ + pch_can_bit_set(&priv->regs->if2_cmask, CAN_CMASK_ALL); + + /* If ID extended is set. */ + pch_can_bit_clear(&priv->regs->if2_id1, 0xffff); + pch_can_bit_clear(&priv->regs->if2_id2, 0x1fff | CAN_ID2_XTD); + if (cf->can_id & CAN_EFF_FLAG) { + pch_can_bit_set(&priv->regs->if2_id1, cf->can_id & 0xffff); + pch_can_bit_set(&priv->regs->if2_id2, + ((cf->can_id >> 16) & 0x1fff) | CAN_ID2_XTD); + } else { + pch_can_bit_set(&priv->regs->if2_id1, 0); + pch_can_bit_set(&priv->regs->if2_id2, + (cf->can_id & CAN_SFF_MASK) << 2); + } + + /* If remote frame has to be transmitted.. */ + if (cf->can_id & CAN_RTR_FLAG) + pch_can_bit_clear(&priv->regs->if2_id2, CAN_ID2_DIR); + + for (i = 0, j = 0; i < cf->can_dlc; j++) { + iowrite32(le32_to_cpu(cf->data[i++]), + (&priv->regs->if2_dataa1) + j*4); + if (i == cf->can_dlc) + break; + iowrite32(le32_to_cpu(cf->data[i++] << 8), + (&priv->regs->if2_dataa1) + j*4); + } + + can_put_echo_skb(skb, ndev, tx_buffer_avail - PCH_RX_OBJ_NUM - 1); + + /* Updating the size of the data. */ + pch_can_bit_clear(&priv->regs->if2_mcont, 0x0f); + pch_can_bit_set(&priv->regs->if2_mcont, cf->can_dlc); + + /* Clearing IntPend, NewDat & TxRqst */ + pch_can_bit_clear(&priv->regs->if2_mcont, + CAN_IF_MCONT_NEWDAT | CAN_IF_MCONT_INTPND | + CAN_IF_MCONT_TXRQXT); + + /* Setting NewDat, TxRqst bits */ + pch_can_bit_set(&priv->regs->if2_mcont, + CAN_IF_MCONT_NEWDAT | CAN_IF_MCONT_TXRQXT); + + pch_can_check_if_busy(&priv->regs->if2_creq, tx_buffer_avail); + + spin_unlock_irqrestore(&priv->msgif_reg_lock, flags); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops pch_can_netdev_ops = { + .ndo_open = pch_can_open, + .ndo_stop = pch_close, + .ndo_start_xmit = pch_xmit, +}; + +static void __devexit pch_can_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(ndev); + + unregister_candev(priv->ndev); + free_candev(priv->ndev); + pci_iounmap(pdev, priv->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + pch_can_reset(priv); +} + +#ifdef CONFIG_PM +static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int i; /* Counter variable. */ + int retval; /* Return value. */ + u32 buf_stat; /* Variable for reading the transmit buffer status. */ + u32 counter = 0xFFFFFF; + + struct net_device *dev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(dev); + + /* Stop the CAN controller */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Indicate that we are aboutto/in suspend */ + priv->can.state = CAN_STATE_SLEEPING; + + /* Waiting for all transmission to complete. */ + while (counter) { + buf_stat = pch_can_get_buffer_status(priv); + if (!buf_stat) + break; + counter--; + udelay(1); + } + if (!counter) + dev_err(&pdev->dev, "%s -> Transmission time out.\n", __func__); + + /* Save interrupt configuration and then disable them */ + pch_can_get_int_enables(priv, &(priv->int_enables)); + pch_can_set_int_enables(priv, PCH_CAN_DISABLE); + + /* Save Tx buffer enable state */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_TX) + pch_can_get_tx_enable(priv, i + 1, + &(priv->tx_enable[i])); + } + + /* Disable all Transmit buffers */ + pch_can_tx_disable_all(priv); + + /* Save Rx buffer enable state */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_RX) { + pch_can_get_rx_enable(priv, i + 1, + &(priv->rx_enable[i])); + pch_can_get_rx_buffer_link(priv, i + 1, + &(priv->rx_link[i])); + } + } + + /* Disable all Receive buffers */ + pch_can_rx_disable_all(priv); + retval = pci_save_state(pdev); + if (retval) { + dev_err(&pdev->dev, "pci_save_state failed.\n"); + } else { + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + } + + return retval; +} + +static int pch_can_resume(struct pci_dev *pdev) +{ + int i; /* Counter variable. */ + int retval; /* Return variable. */ + struct net_device *dev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(dev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "pci_enable_device failed.\n"); + return retval; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Disabling all interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_DISABLE); + + /* Setting the CAN device in Stop Mode. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Configuring the transmit and receive buffers. */ + pch_can_config_rx_tx_buffers(priv); + + /* Restore the CAN state */ + pch_set_bittiming(dev); + + /* Listen/Active */ + pch_can_set_optmode(priv); + + /* Enabling the transmit buffer. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_TX) { + pch_can_set_tx_enable(priv, i + 1, + priv->tx_enable[i]); + } + } + + /* Configuring the receive buffer and enabling them. */ + for (i = 0; i < PCH_OBJ_NUM; i++) { + if (priv->msg_obj[i] == MSG_OBJ_RX) { + /* Restore buffer link */ + pch_can_set_rx_buffer_link(priv, i + 1, + priv->rx_link[i]); + + /* Restore buffer enables */ + pch_can_set_rx_enable(priv, i + 1, priv->rx_enable[i]); + } + } + + /* Enable CAN Interrupts */ + pch_can_set_int_custom(priv); + + /* Restore Run Mode */ + pch_can_set_run_mode(priv, PCH_CAN_RUN); + + return retval; +} +#else +#define pch_can_suspend NULL +#define pch_can_resume NULL +#endif + +static int pch_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct pch_can_priv *priv = netdev_priv(dev); + + bec->txerr = ioread32(&priv->regs->errc) & CAN_TEC; + bec->rxerr = (ioread32(&priv->regs->errc) & CAN_REC) >> 8; + + return 0; +} + +static int __devinit pch_can_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct net_device *ndev; + struct pch_can_priv *priv; + int rc; + int index; + void __iomem *addr; + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "Failed pci_enable_device %d\n", rc); + goto probe_exit_endev; + } + + rc = pci_request_regions(pdev, KBUILD_MODNAME); + if (rc) { + dev_err(&pdev->dev, "Failed pci_request_regions %d\n", rc); + goto probe_exit_pcireq; + } + + addr = pci_iomap(pdev, 1, 0); + if (!addr) { + rc = -EIO; + dev_err(&pdev->dev, "Failed pci_iomap\n"); + goto probe_exit_ipmap; + } + + ndev = alloc_candev(sizeof(struct pch_can_priv), PCH_TX_OBJ_NUM); + if (!ndev) { + rc = -ENOMEM; + dev_err(&pdev->dev, "Failed alloc_candev\n"); + goto probe_exit_alloc_candev; + } + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->regs = addr; + priv->dev = pdev; + priv->can.bittiming_const = &pch_can_bittiming_const; + priv->can.do_set_mode = pch_can_do_set_mode; + priv->can.do_get_berr_counter = pch_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK; + priv->tx_obj = PCH_RX_OBJ_NUM + 1; /* Point head of Tx Obj */ + + ndev->irq = pdev->irq; + ndev->flags |= IFF_ECHO; + + pci_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &pch_can_netdev_ops; + + priv->can.clock.freq = PCH_CAN_CLK; /* Hz */ + for (index = 0; index < PCH_RX_OBJ_NUM;) + priv->msg_obj[index++] = MSG_OBJ_RX; + + for (index = index; index < PCH_OBJ_NUM;) + priv->msg_obj[index++] = MSG_OBJ_TX; + + netif_napi_add(ndev, &priv->napi, pch_can_rx_poll, PCH_RX_OBJ_NUM); + + rc = register_candev(ndev); + if (rc) { + dev_err(&pdev->dev, "Failed register_candev %d\n", rc); + goto probe_exit_reg_candev; + } + + return 0; + +probe_exit_reg_candev: + free_candev(ndev); +probe_exit_alloc_candev: + pci_iounmap(pdev, addr); +probe_exit_ipmap: + pci_release_regions(pdev); +probe_exit_pcireq: + pci_disable_device(pdev); +probe_exit_endev: + return rc; +} + +static struct pci_driver pch_can_pcidev = { + .name = "pch_can", + .id_table = pch_pci_tbl, + .probe = pch_can_probe, + .remove = __devexit_p(pch_can_remove), + .suspend = pch_can_suspend, + .resume = pch_can_resume, +}; + +static int __init pch_can_pci_init(void) +{ + return pci_register_driver(&pch_can_pcidev); +} +module_init(pch_can_pci_init); + +static void __exit pch_can_pci_exit(void) +{ + pci_unregister_driver(&pch_can_pcidev); +} +module_exit(pch_can_pci_exit); + +MODULE_DESCRIPTION("Controller Area Network Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.94"); diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index ae3505afd682..6fdc031daaae 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -58,4 +58,16 @@ config CAN_PLX_PCI - esd CAN-PCIe/2000 - Marathon CAN-bus-PCI card (http://www.marathon.ru/) - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/) + +config CAN_TSCAN1 + tristate "TS-CAN1 PC104 boards" + depends on ISA + help + This driver is for Technologic Systems' TSCAN-1 PC104 boards. + http://www.embeddedarm.com/products/board-detail.php?product=TS-CAN1 + The driver supports multiple boards and automatically configures them: + PLD IO base addresses are read from jumpers JP1 and JP2, + IRQ numbers are read from jumpers JP4 and JP5, + SJA1000 IO base addresses are chosen heuristically (first that works). + endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index ce924553995d..2c591eb321c7 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o +obj-$(CONFIG_CAN_TSCAN1) += tscan1.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/tscan1.c b/drivers/net/can/sja1000/tscan1.c new file mode 100644 index 000000000000..9756099a883a --- /dev/null +++ b/drivers/net/can/sja1000/tscan1.c @@ -0,0 +1,216 @@ +/* + * tscan1.c: driver for Technologic Systems TS-CAN1 PC104 boards + * + * Copyright 2010 Andre B. Oliveira + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * References: + * - Getting started with TS-CAN1, Technologic Systems, Jun 2009 + * http://www.embeddedarm.com/documentation/ts-can1-manual.pdf + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/isa.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include "sja1000.h" + +MODULE_DESCRIPTION("Driver for Technologic Systems TS-CAN1 PC104 boards"); +MODULE_AUTHOR("Andre B. Oliveira <anbadeol@gmail.com>"); +MODULE_LICENSE("GPL"); + +/* Maximum number of boards (one in each JP1:JP2 setting of IO address) */ +#define TSCAN1_MAXDEV 4 + +/* PLD registers address offsets */ +#define TSCAN1_ID1 0 +#define TSCAN1_ID2 1 +#define TSCAN1_VERSION 2 +#define TSCAN1_LED 3 +#define TSCAN1_PAGE 4 +#define TSCAN1_MODE 5 +#define TSCAN1_JUMPERS 6 + +/* PLD board identifier registers magic values */ +#define TSCAN1_ID1_VALUE 0xf6 +#define TSCAN1_ID2_VALUE 0xb9 + +/* PLD mode register SJA1000 IO enable bit */ +#define TSCAN1_MODE_ENABLE 0x40 + +/* PLD jumpers register bits */ +#define TSCAN1_JP4 0x10 +#define TSCAN1_JP5 0x20 + +/* PLD IO base addresses start */ +#define TSCAN1_PLD_ADDRESS 0x150 + +/* PLD register space size */ +#define TSCAN1_PLD_SIZE 8 + +/* SJA1000 register space size */ +#define TSCAN1_SJA1000_SIZE 32 + +/* SJA1000 crystal frequency (16MHz) */ +#define TSCAN1_SJA1000_XTAL 16000000 + +/* SJA1000 IO base addresses */ +static const unsigned short tscan1_sja1000_addresses[] __devinitconst = { + 0x100, 0x120, 0x180, 0x1a0, 0x200, 0x240, 0x280, 0x320 +}; + +/* Read SJA1000 register */ +static u8 tscan1_read(const struct sja1000_priv *priv, int reg) +{ + return inb((unsigned long)priv->reg_base + reg); +} + +/* Write SJA1000 register */ +static void tscan1_write(const struct sja1000_priv *priv, int reg, u8 val) +{ + outb(val, (unsigned long)priv->reg_base + reg); +} + +/* Probe for a TS-CAN1 board with JP2:JP1 jumper setting ID */ +static int __devinit tscan1_probe(struct device *dev, unsigned id) +{ + struct net_device *netdev; + struct sja1000_priv *priv; + unsigned long pld_base, sja1000_base; + int irq, i; + + pld_base = TSCAN1_PLD_ADDRESS + id * TSCAN1_PLD_SIZE; + if (!request_region(pld_base, TSCAN1_PLD_SIZE, dev_name(dev))) + return -EBUSY; + + if (inb(pld_base + TSCAN1_ID1) != TSCAN1_ID1_VALUE || + inb(pld_base + TSCAN1_ID2) != TSCAN1_ID2_VALUE) { + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENODEV; + } + + switch (inb(pld_base + TSCAN1_JUMPERS) & (TSCAN1_JP4 | TSCAN1_JP5)) { + case TSCAN1_JP4: + irq = 6; + break; + case TSCAN1_JP5: + irq = 7; + break; + case TSCAN1_JP4 | TSCAN1_JP5: + irq = 5; + break; + default: + dev_err(dev, "invalid JP4:JP5 setting (no IRQ)\n"); + release_region(pld_base, TSCAN1_PLD_SIZE); + return -EINVAL; + } + + netdev = alloc_sja1000dev(0); + if (!netdev) { + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENOMEM; + } + + dev_set_drvdata(dev, netdev); + SET_NETDEV_DEV(netdev, dev); + + netdev->base_addr = pld_base; + netdev->irq = irq; + + priv = netdev_priv(netdev); + priv->read_reg = tscan1_read; + priv->write_reg = tscan1_write; + priv->can.clock.freq = TSCAN1_SJA1000_XTAL / 2; + priv->cdr = CDR_CBP | CDR_CLK_OFF; + priv->ocr = OCR_TX0_PUSHPULL; + + /* Select the first SJA1000 IO address that is free and that works */ + for (i = 0; i < ARRAY_SIZE(tscan1_sja1000_addresses); i++) { + sja1000_base = tscan1_sja1000_addresses[i]; + if (!request_region(sja1000_base, TSCAN1_SJA1000_SIZE, + dev_name(dev))) + continue; + + /* Set SJA1000 IO base address and enable it */ + outb(TSCAN1_MODE_ENABLE | i, pld_base + TSCAN1_MODE); + + priv->reg_base = (void __iomem *)sja1000_base; + if (!register_sja1000dev(netdev)) { + /* SJA1000 probe succeeded; turn LED off and return */ + outb(0, pld_base + TSCAN1_LED); + netdev_info(netdev, "TS-CAN1 at 0x%lx 0x%lx irq %d\n", + pld_base, sja1000_base, irq); + return 0; + } + + /* SJA1000 probe failed; release and try next address */ + outb(0, pld_base + TSCAN1_MODE); + release_region(sja1000_base, TSCAN1_SJA1000_SIZE); + } + + dev_err(dev, "failed to assign SJA1000 IO address\n"); + dev_set_drvdata(dev, NULL); + free_sja1000dev(netdev); + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENXIO; +} + +static int __devexit tscan1_remove(struct device *dev, unsigned id /*unused*/) +{ + struct net_device *netdev; + struct sja1000_priv *priv; + unsigned long pld_base, sja1000_base; + + netdev = dev_get_drvdata(dev); + unregister_sja1000dev(netdev); + dev_set_drvdata(dev, NULL); + + priv = netdev_priv(netdev); + pld_base = netdev->base_addr; + sja1000_base = (unsigned long)priv->reg_base; + + outb(0, pld_base + TSCAN1_MODE); /* disable SJA1000 IO space */ + + release_region(sja1000_base, TSCAN1_SJA1000_SIZE); + release_region(pld_base, TSCAN1_PLD_SIZE); + + free_sja1000dev(netdev); + + return 0; +} + +static struct isa_driver tscan1_isa_driver = { + .probe = tscan1_probe, + .remove = __devexit_p(tscan1_remove), + .driver = { + .name = "tscan1", + }, +}; + +static int __init tscan1_init(void) +{ + return isa_register_driver(&tscan1_isa_driver, TSCAN1_MAXDEV); +} +module_init(tscan1_init); + +static void __exit tscan1_exit(void) +{ + isa_unregister_driver(&tscan1_isa_driver); +} +module_exit(tscan1_exit); |