diff options
Diffstat (limited to 'drivers/net/can/ti_hecc.c')
-rw-r--r-- | drivers/net/can/ti_hecc.c | 73 |
1 files changed, 68 insertions, 5 deletions
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 5c993c2da528..0c3d2ba0d178 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -28,9 +28,11 @@ * .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. * */ @@ -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) @@ -317,6 +320,13 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) 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; @@ -477,6 +487,9 @@ 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); @@ -491,7 +504,6 @@ 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); if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ data |= HECC_CANMCF_RTR; data |= get_tx_head_prio(priv) << 8; @@ -816,15 +828,17 @@ static int ti_hecc_open(struct net_device *ndev) 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); + ti_hecc_transceiver_switch(priv, 0); free_irq(ndev->irq, ndev); return err; } - clk_enable(priv->clk); ti_hecc_start(ndev); napi_enable(&priv->napi); netif_start_queue(ndev); @@ -840,8 +854,8 @@ 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); return 0; } @@ -903,10 +917,12 @@ 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.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; ndev->irq = irq->start; ndev->flags |= IFF_ECHO; @@ -925,6 +941,7 @@ 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"); @@ -953,6 +970,7 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); + clk_disable(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); @@ -964,6 +982,48 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev) 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 = { @@ -972,6 +1032,8 @@ static struct platform_driver ti_hecc_driver = { }, .probe = ti_hecc_probe, .remove = __devexit_p(ti_hecc_remove), + .suspend = ti_hecc_suspend, + .resume = ti_hecc_resume, }; static int __init ti_hecc_init_driver(void) @@ -979,14 +1041,15 @@ 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_init(ti_hecc_init_driver); MODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); MODULE_LICENSE("GPL v2"); |