diff options
Diffstat (limited to 'drivers/net/can/ti_hecc.c')
| -rw-r--r-- | drivers/net/can/ti_hecc.c | 174 |
1 files changed, 118 insertions, 56 deletions
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 5c993c2da52..258b9c4856e 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -28,14 +28,15 @@ * .mbx_offset = 0x2000, * .int_line = 0, * .revision = 1, + * .transceiver_switch = hecc_phy_control, * }; * - * Please see include/can/platform/ti_hecc.h for description of above fields + * Please see include/linux/can/platform/ti_hecc.h for description of + * above fields. * */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/interrupt.h> @@ -44,10 +45,11 @@ #include <linux/skbuff.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/io.h> -#include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/can/led.h> #include <linux/can/platform/ti_hecc.h> #define DRV_NAME "ti_hecc" @@ -194,7 +196,7 @@ MODULE_VERSION(HECC_MODULE_VERSION); #define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ /* CAN Bittiming constants as per HECC specs */ -static struct can_bittiming_const ti_hecc_bittiming_const = { +static const struct can_bittiming_const ti_hecc_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, @@ -220,6 +222,7 @@ struct ti_hecc_priv { u32 tx_head; u32 tx_tail; u32 rx_next; + void (*transceiver_switch)(int); }; static inline int get_tx_head_mb(struct ti_hecc_priv *priv) @@ -282,15 +285,6 @@ static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; } -static int ti_hecc_get_state(const struct net_device *ndev, - enum can_state *state) -{ - struct ti_hecc_priv *priv = netdev_priv(ndev); - - *state = priv->can.state; - return 0; -} - static int ti_hecc_set_btc(struct ti_hecc_priv *priv) { struct can_bittiming *bit_timing = &priv->can.bittiming; @@ -303,7 +297,7 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) if (bit_timing->brp > 4) can_btc |= HECC_CANBTC_SAM; else - dev_warn(priv->ndev->dev.parent, "WARN: Triple" \ + netdev_warn(priv->ndev, "WARN: Triple" "sampling not set due to h/w limitations"); } can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; @@ -312,17 +306,24 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) /* ERM being set to 0 by default meaning resync at falling edge */ hecc_write(priv, HECC_CANBTC, can_btc); - dev_info(priv->ndev->dev.parent, "setting CANBTC=%#x\n", can_btc); + netdev_info(priv->ndev, "setting CANBTC=%#x\n", can_btc); return 0; } +static void ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, + int on) +{ + if (priv->transceiver_switch) + priv->transceiver_switch(on); +} + static void ti_hecc_reset(struct net_device *ndev) { u32 cnt; struct ti_hecc_priv *priv = netdev_priv(ndev); - dev_dbg(ndev->dev.parent, "resetting hecc ...\n"); + netdev_dbg(ndev, "resetting hecc ...\n"); hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); /* Set change control request and wait till enabled */ @@ -448,6 +449,17 @@ static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) return ret; } +static int ti_hecc_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + bec->txerr = hecc_read(priv, HECC_CANTEC); + bec->rxerr = hecc_read(priv, HECC_CANREC); + + return 0; +} + /* * ti_hecc_xmit: HECC Transmit * @@ -477,13 +489,16 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) u32 mbxno, mbx_mask, data; unsigned long flags; + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + mbxno = get_tx_head_mb(priv); mbx_mask = BIT(mbxno); spin_lock_irqsave(&priv->mbx_lock, flags); if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { spin_unlock_irqrestore(&priv->mbx_lock, flags); netif_stop_queue(ndev); - dev_err(priv->ndev->dev.parent, + netdev_err(priv->ndev, "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n", priv->tx_head, priv->tx_tail); return NETDEV_TX_BUSY; @@ -491,10 +506,9 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) spin_unlock_irqrestore(&priv->mbx_lock, flags); /* Prepare mailbox for transmission */ - data = min_t(u8, cf->can_dlc, 8); + data = cf->can_dlc | (get_tx_head_prio(priv) << 8); if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ data |= HECC_CANMCF_RTR; - data |= get_tx_head_prio(priv) << 8; hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ @@ -503,10 +517,10 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) data = (cf->can_id & CAN_SFF_MASK) << 18; hecc_write_mbx(priv, mbxno, HECC_CANMID, data); hecc_write_mbx(priv, mbxno, HECC_CANMDL, - be32_to_cpu(*(u32 *)(cf->data))); + be32_to_cpu(*(__be32 *)(cf->data))); if (cf->can_dlc > 4) hecc_write_mbx(priv, mbxno, HECC_CANMDH, - be32_to_cpu(*(u32 *)(cf->data + 4))); + be32_to_cpu(*(__be32 *)(cf->data + 4))); else *(u32 *)(cf->data + 4) = 0; can_put_echo_skb(skb, ndev, mbxno); @@ -538,7 +552,7 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) skb = alloc_can_skb(priv->ndev, &cf); if (!skb) { if (printk_ratelimit()) - dev_err(priv->ndev->dev.parent, + netdev_err(priv->ndev, "ti_hecc_rx_pkt: alloc_can_skb() failed\n"); return -ENOMEM; } @@ -554,12 +568,10 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) cf->can_id |= CAN_RTR_FLAG; cf->can_dlc = get_can_dlc(data & 0xF); data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); - *(u32 *)(cf->data) = cpu_to_be32(data); + *(__be32 *)(cf->data) = cpu_to_be32(data); if (cf->can_dlc > 4) { data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); - *(u32 *)(cf->data + 4) = cpu_to_be32(data); - } else { - *(u32 *)(cf->data + 4) = 0; + *(__be32 *)(cf->data + 4) = cpu_to_be32(data); } spin_lock_irqsave(&priv->mbx_lock, flags); hecc_clear_bit(priv, HECC_CANME, mbx_mask); @@ -570,6 +582,7 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) spin_unlock_irqrestore(&priv->mbx_lock, flags); stats->rx_bytes += cf->can_dlc; + can_led_event(priv->ndev, CAN_LED_EVENT_RX); netif_receive_skb(skb); stats->rx_packets++; @@ -652,11 +665,11 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, struct can_frame *cf; struct sk_buff *skb; - /* propogate the error condition to the can stack */ + /* propagate the error condition to the can stack */ skb = alloc_can_err_skb(ndev, &cf); if (!skb) { if (printk_ratelimit()) - dev_err(priv->ndev->dev.parent, + netdev_err(priv->ndev, "ti_hecc_error: alloc_can_err_skb() failed\n"); return -ENOMEM; } @@ -672,7 +685,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; } hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW); - dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n"); + netdev_dbg(priv->ndev, "Error Warning interrupt\n"); hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); } @@ -687,7 +700,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; } hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP); - dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + netdev_dbg(priv->ndev, "Error passive interrupt\n"); hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); } @@ -723,19 +736,20 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, } if (err_status & HECC_CANES_CRCE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); - cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL; } if (err_status & HECC_CANES_ACKE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); - cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + cf->data[3] |= CAN_ERR_PROT_LOC_ACK | CAN_ERR_PROT_LOC_ACK_DEL; } } - netif_receive_skb(skb); + netif_rx(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + return 0; } @@ -772,6 +786,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) stats->tx_bytes += hecc_read_mbx(priv, mbxno, HECC_CANMCF) & 0xF; stats->tx_packets++; + can_led_event(ndev, CAN_LED_EVENT_TX); can_get_echo_skb(ndev, mbxno); --priv->tx_tail; } @@ -812,19 +827,23 @@ static int ti_hecc_open(struct net_device *ndev) err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, ndev->name, ndev); if (err) { - dev_err(ndev->dev.parent, "error requesting interrupt\n"); + netdev_err(ndev, "error requesting interrupt\n"); return err; } + ti_hecc_transceiver_switch(priv, 1); + /* Open common can device */ err = open_candev(ndev); if (err) { - dev_err(ndev->dev.parent, "open_candev() failed %d\n", err); + netdev_err(ndev, "open_candev() failed %d\n", err); + ti_hecc_transceiver_switch(priv, 0); free_irq(ndev->irq, ndev); return err; } - clk_enable(priv->clk); + can_led_event(ndev, CAN_LED_EVENT_OPEN); + ti_hecc_start(ndev); napi_enable(&priv->napi); netif_start_queue(ndev); @@ -840,8 +859,10 @@ static int ti_hecc_close(struct net_device *ndev) napi_disable(&priv->napi); ti_hecc_stop(ndev); free_irq(ndev->irq, ndev); - clk_disable(priv->clk); close_candev(ndev); + ti_hecc_transceiver_switch(priv, 0); + + can_led_event(ndev, CAN_LED_EVENT_STOP); return 0; } @@ -850,6 +871,7 @@ static const struct net_device_ops ti_hecc_netdev_ops = { .ndo_open = ti_hecc_open, .ndo_stop = ti_hecc_close, .ndo_start_xmit = ti_hecc_xmit, + .ndo_change_mtu = can_change_mtu, }; static int ti_hecc_probe(struct platform_device *pdev) @@ -861,7 +883,7 @@ static int ti_hecc_probe(struct platform_device *pdev) void __iomem *addr; int err = -ENODEV; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data\n"); goto probe_exit; @@ -903,11 +925,14 @@ static int ti_hecc_probe(struct platform_device *pdev) priv->hecc_ram_offset = pdata->hecc_ram_offset; priv->mbx_offset = pdata->mbx_offset; priv->int_line = pdata->int_line; + priv->transceiver_switch = pdata->transceiver_switch; priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; - priv->can.do_get_state = ti_hecc_get_state; + priv->can.do_get_berr_counter = ti_hecc_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + spin_lock_init(&priv->mbx_lock); ndev->irq = irq->start; ndev->flags |= IFF_ECHO; platform_set_drvdata(pdev, ndev); @@ -925,11 +950,15 @@ static int ti_hecc_probe(struct platform_device *pdev) netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, HECC_DEF_NAPI_WEIGHT); + clk_enable(priv->clk); err = register_candev(ndev); if (err) { dev_err(&pdev->dev, "register_candev() failed\n"); goto probe_exit_clk; } + + devm_can_led_init(ndev); + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", priv->base, (u32) ndev->irq); @@ -947,23 +976,65 @@ probe_exit: return err; } -static int __devexit ti_hecc_remove(struct platform_device *pdev) +static int ti_hecc_remove(struct platform_device *pdev) { struct resource *res; struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); + unregister_candev(ndev); + clk_disable(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); release_mem_region(res->start, resource_size(res)); - unregister_candev(ndev); free_candev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } + +#ifdef CONFIG_PM +static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + + return 0; +} + +static int ti_hecc_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + clk_enable(priv->clk); + + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#else +#define ti_hecc_suspend NULL +#define ti_hecc_resume NULL +#endif + /* TI HECC netdevice driver: platform driver structure */ static struct platform_driver ti_hecc_driver = { .driver = { @@ -971,23 +1042,14 @@ static struct platform_driver ti_hecc_driver = { .owner = THIS_MODULE, }, .probe = ti_hecc_probe, - .remove = __devexit_p(ti_hecc_remove), + .remove = ti_hecc_remove, + .suspend = ti_hecc_suspend, + .resume = ti_hecc_resume, }; -static int __init ti_hecc_init_driver(void) -{ - printk(KERN_INFO DRV_DESC "\n"); - return platform_driver_register(&ti_hecc_driver); -} -module_init(ti_hecc_init_driver); - -static void __exit ti_hecc_exit_driver(void) -{ - printk(KERN_INFO DRV_DESC " unloaded\n"); - platform_driver_unregister(&ti_hecc_driver); -} -module_exit(ti_hecc_exit_driver); +module_platform_driver(ti_hecc_driver); MODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:" DRV_NAME); |
