Commit 09ea2679 authored by David S. Miller's avatar David S. Miller

Merge tag 'linux-can-fixes-for-5.3-20190724' of...

Merge tag 'linux-can-fixes-for-5.3-20190724' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can

Marc Kleine-Budde says:

====================
pull-request: can 2019-07-24

this is a pull reqeust of 7 patches for net/master.

The first patch is by Rasmus Villemoes add a missing netif_carrier_off() to
register_candev() so that generic netdev trigger based LEDs are initially off.

Nikita Yushchenko's patch for the rcar_canfd driver fixes a possible IRQ storm
on high load.

The patch by Weitao Hou for the mcp251x driver add missing error checking to
the work queue allocation.

Both Wen Yang's and Joakim Zhang's patch for the flexcan driver fix a problem
with the stop-mode.

Stephane Grosjean contributes a patch for the peak_usb driver to fix a
potential double kfree_skb().

The last patch is by YueHaibing and fixes the error path in can-gw's
cgw_module_init() function.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3bc817d6 b7a14297
...@@ -1249,6 +1249,8 @@ int register_candev(struct net_device *dev) ...@@ -1249,6 +1249,8 @@ int register_candev(struct net_device *dev)
return -EINVAL; return -EINVAL;
dev->rtnl_link_ops = &can_link_ops; dev->rtnl_link_ops = &can_link_ops;
netif_carrier_off(dev);
return register_netdev(dev); return register_netdev(dev);
} }
EXPORT_SYMBOL_GPL(register_candev); EXPORT_SYMBOL_GPL(register_candev);
......
...@@ -400,9 +400,10 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable) ...@@ -400,9 +400,10 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
priv->write(reg_mcr, &regs->mcr); priv->write(reg_mcr, &regs->mcr);
} }
static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv) static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
{ {
struct flexcan_regs __iomem *regs = priv->regs; struct flexcan_regs __iomem *regs = priv->regs;
unsigned int ackval;
u32 reg_mcr; u32 reg_mcr;
reg_mcr = priv->read(&regs->mcr); reg_mcr = priv->read(&regs->mcr);
...@@ -412,20 +413,37 @@ static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv) ...@@ -412,20 +413,37 @@ static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
/* enable stop request */ /* enable stop request */
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
1 << priv->stm.req_bit, 1 << priv->stm.req_bit); 1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
/* get stop acknowledgment */
if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr,
ackval, ackval & (1 << priv->stm.ack_bit),
0, FLEXCAN_TIMEOUT_US))
return -ETIMEDOUT;
return 0;
} }
static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv) static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
{ {
struct flexcan_regs __iomem *regs = priv->regs; struct flexcan_regs __iomem *regs = priv->regs;
unsigned int ackval;
u32 reg_mcr; u32 reg_mcr;
/* remove stop request */ /* remove stop request */
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
1 << priv->stm.req_bit, 0); 1 << priv->stm.req_bit, 0);
/* get stop acknowledgment */
if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr,
ackval, !(ackval & (1 << priv->stm.ack_bit)),
0, FLEXCAN_TIMEOUT_US))
return -ETIMEDOUT;
reg_mcr = priv->read(&regs->mcr); reg_mcr = priv->read(&regs->mcr);
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
priv->write(reg_mcr, &regs->mcr); priv->write(reg_mcr, &regs->mcr);
return 0;
} }
static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv) static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
...@@ -1437,10 +1455,10 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ...@@ -1437,10 +1455,10 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
priv = netdev_priv(dev); priv = netdev_priv(dev);
priv->stm.gpr = syscon_node_to_regmap(gpr_np); priv->stm.gpr = syscon_node_to_regmap(gpr_np);
of_node_put(gpr_np);
if (IS_ERR(priv->stm.gpr)) { if (IS_ERR(priv->stm.gpr)) {
dev_dbg(&pdev->dev, "could not find gpr regmap\n"); dev_dbg(&pdev->dev, "could not find gpr regmap\n");
return PTR_ERR(priv->stm.gpr); ret = PTR_ERR(priv->stm.gpr);
goto out_put_node;
} }
priv->stm.req_gpr = out_val[1]; priv->stm.req_gpr = out_val[1];
...@@ -1455,7 +1473,9 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ...@@ -1455,7 +1473,9 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true); device_set_wakeup_capable(&pdev->dev, true);
return 0; out_put_node:
of_node_put(gpr_np);
return ret;
} }
static const struct of_device_id flexcan_of_match[] = { static const struct of_device_id flexcan_of_match[] = {
...@@ -1612,7 +1632,9 @@ static int __maybe_unused flexcan_suspend(struct device *device) ...@@ -1612,7 +1632,9 @@ static int __maybe_unused flexcan_suspend(struct device *device)
*/ */
if (device_may_wakeup(device)) { if (device_may_wakeup(device)) {
enable_irq_wake(dev->irq); enable_irq_wake(dev->irq);
flexcan_enter_stop_mode(priv); err = flexcan_enter_stop_mode(priv);
if (err)
return err;
} else { } else {
err = flexcan_chip_disable(priv); err = flexcan_chip_disable(priv);
if (err) if (err)
...@@ -1662,10 +1684,13 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) ...@@ -1662,10 +1684,13 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device)
{ {
struct net_device *dev = dev_get_drvdata(device); struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_priv *priv = netdev_priv(dev);
int err;
if (netif_running(dev) && device_may_wakeup(device)) { if (netif_running(dev) && device_may_wakeup(device)) {
flexcan_enable_wakeup_irq(priv, false); flexcan_enable_wakeup_irq(priv, false);
flexcan_exit_stop_mode(priv); err = flexcan_exit_stop_mode(priv);
if (err)
return err;
} }
return 0; return 0;
......
...@@ -1508,10 +1508,11 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) ...@@ -1508,10 +1508,11 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota)
/* All packets processed */ /* All packets processed */
if (num_pkts < quota) { if (num_pkts < quota) {
napi_complete_done(napi, num_pkts); if (napi_complete_done(napi, num_pkts)) {
/* Enable Rx FIFO interrupts */ /* Enable Rx FIFO interrupts */
rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx),
RCANFD_RFCC_RFIE); RCANFD_RFCC_RFIE);
}
} }
return num_pkts; return num_pkts;
} }
......
...@@ -664,17 +664,6 @@ static int mcp251x_power_enable(struct regulator *reg, int enable) ...@@ -664,17 +664,6 @@ static int mcp251x_power_enable(struct regulator *reg, int enable)
return regulator_disable(reg); return regulator_disable(reg);
} }
static void mcp251x_open_clean(struct net_device *net)
{
struct mcp251x_priv *priv = netdev_priv(net);
struct spi_device *spi = priv->spi;
free_irq(spi->irq, priv);
mcp251x_hw_sleep(spi);
mcp251x_power_enable(priv->transceiver, 0);
close_candev(net);
}
static int mcp251x_stop(struct net_device *net) static int mcp251x_stop(struct net_device *net)
{ {
struct mcp251x_priv *priv = netdev_priv(net); struct mcp251x_priv *priv = netdev_priv(net);
...@@ -940,37 +929,43 @@ static int mcp251x_open(struct net_device *net) ...@@ -940,37 +929,43 @@ static int mcp251x_open(struct net_device *net)
flags | IRQF_ONESHOT, DEVICE_NAME, priv); flags | IRQF_ONESHOT, DEVICE_NAME, priv);
if (ret) { if (ret) {
dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
mcp251x_power_enable(priv->transceiver, 0); goto out_close;
close_candev(net);
goto open_unlock;
} }
priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
0); 0);
if (!priv->wq) {
ret = -ENOMEM;
goto out_clean;
}
INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler);
ret = mcp251x_hw_reset(spi); ret = mcp251x_hw_reset(spi);
if (ret) { if (ret)
mcp251x_open_clean(net); goto out_free_wq;
goto open_unlock;
}
ret = mcp251x_setup(net, spi); ret = mcp251x_setup(net, spi);
if (ret) { if (ret)
mcp251x_open_clean(net); goto out_free_wq;
goto open_unlock;
}
ret = mcp251x_set_normal_mode(spi); ret = mcp251x_set_normal_mode(spi);
if (ret) { if (ret)
mcp251x_open_clean(net); goto out_free_wq;
goto open_unlock;
}
can_led_event(net, CAN_LED_EVENT_OPEN); can_led_event(net, CAN_LED_EVENT_OPEN);
netif_wake_queue(net); netif_wake_queue(net);
mutex_unlock(&priv->mcp_lock);
open_unlock: return 0;
out_free_wq:
destroy_workqueue(priv->wq);
out_clean:
free_irq(spi->irq, priv);
mcp251x_hw_sleep(spi);
out_close:
mcp251x_power_enable(priv->transceiver, 0);
close_candev(net);
mutex_unlock(&priv->mcp_lock); mutex_unlock(&priv->mcp_lock);
return ret; return ret;
} }
......
...@@ -568,16 +568,16 @@ static int peak_usb_ndo_stop(struct net_device *netdev) ...@@ -568,16 +568,16 @@ static int peak_usb_ndo_stop(struct net_device *netdev)
dev->state &= ~PCAN_USB_STATE_STARTED; dev->state &= ~PCAN_USB_STATE_STARTED;
netif_stop_queue(netdev); netif_stop_queue(netdev);
close_candev(netdev);
dev->can.state = CAN_STATE_STOPPED;
/* unlink all pending urbs and free used memory */ /* unlink all pending urbs and free used memory */
peak_usb_unlink_all_urbs(dev); peak_usb_unlink_all_urbs(dev);
if (dev->adapter->dev_stop) if (dev->adapter->dev_stop)
dev->adapter->dev_stop(dev); dev->adapter->dev_stop(dev);
close_candev(netdev);
dev->can.state = CAN_STATE_STOPPED;
/* can set bus off now */ /* can set bus off now */
if (dev->adapter->dev_set_bus) { if (dev->adapter->dev_set_bus) {
int err = dev->adapter->dev_set_bus(dev, 0); int err = dev->adapter->dev_set_bus(dev, 0);
......
...@@ -1046,32 +1046,50 @@ static __init int cgw_module_init(void) ...@@ -1046,32 +1046,50 @@ static __init int cgw_module_init(void)
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
max_hops); max_hops);
register_pernet_subsys(&cangw_pernet_ops); ret = register_pernet_subsys(&cangw_pernet_ops);
if (ret)
return ret;
ret = -ENOMEM;
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
0, 0, NULL); 0, 0, NULL);
if (!cgw_cache) if (!cgw_cache)
return -ENOMEM; goto out_cache_create;
/* set notifier */ /* set notifier */
notifier.notifier_call = cgw_notifier; notifier.notifier_call = cgw_notifier;
register_netdevice_notifier(&notifier); ret = register_netdevice_notifier(&notifier);
if (ret)
goto out_register_notifier;
ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE, ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
NULL, cgw_dump_jobs, 0); NULL, cgw_dump_jobs, 0);
if (ret) { if (ret)
unregister_netdevice_notifier(&notifier); goto out_rtnl_register1;
kmem_cache_destroy(cgw_cache);
return -ENOBUFS; ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
} cgw_create_job, NULL, 0);
if (ret)
/* Only the first call to rtnl_register_module can fail */ goto out_rtnl_register2;
rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE, ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
cgw_create_job, NULL, 0); cgw_remove_job, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE, if (ret)
cgw_remove_job, NULL, 0); goto out_rtnl_register3;
return 0; return 0;
out_rtnl_register3:
rtnl_unregister(PF_CAN, RTM_NEWROUTE);
out_rtnl_register2:
rtnl_unregister(PF_CAN, RTM_GETROUTE);
out_rtnl_register1:
unregister_netdevice_notifier(&notifier);
out_register_notifier:
kmem_cache_destroy(cgw_cache);
out_cache_create:
unregister_pernet_subsys(&cangw_pernet_ops);
return ret;
} }
static __exit void cgw_module_exit(void) static __exit void cgw_module_exit(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment