mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-27 04:54:41 +08:00
Merge patch series "can: m_can: Fix polling and other issues"
Markus Schneider-Pargmann <msp@baylibre.com> says: these are a number of fixes for m_can that fix polling mode and some other issues that I saw while working on the code. Changes in v2 ------------- - Fixed one multiline comment - Rebased to v6.11-rc1 Previous versions ----------------- v1: https://lore.kernel.org/lkml/20240726195944.2414812-1-msp@baylibre.com/ Link: https://lore.kernel.org/all/20240805183047.305630-1-msp@baylibre.com Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
commit
053ae05f80
@ -483,11 +483,10 @@ static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev)
|
||||
{
|
||||
m_can_coalescing_disable(cdev);
|
||||
m_can_write(cdev, M_CAN_ILE, 0x0);
|
||||
cdev->active_interrupts = 0x0;
|
||||
|
||||
if (!cdev->net->irq) {
|
||||
dev_dbg(cdev->dev, "Stop hrtimer\n");
|
||||
hrtimer_cancel(&cdev->hrtimer);
|
||||
hrtimer_try_to_cancel(&cdev->hrtimer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1037,22 +1036,6 @@ end:
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static int m_can_rx_peripheral(struct net_device *dev, u32 irqstatus)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int work_done;
|
||||
|
||||
work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, irqstatus);
|
||||
|
||||
/* Don't re-enable interrupts if the driver had a fatal error
|
||||
* (e.g., FIFO read failure).
|
||||
*/
|
||||
if (work_done < 0)
|
||||
m_can_disable_all_interrupts(cdev);
|
||||
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static int m_can_poll(struct napi_struct *napi, int quota)
|
||||
{
|
||||
struct net_device *dev = napi->dev;
|
||||
@ -1217,16 +1200,18 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
/* This interrupt handler is called either from the interrupt thread or a
|
||||
* hrtimer. This has implications like cancelling a timer won't be possible
|
||||
* blocking.
|
||||
*/
|
||||
static int m_can_interrupt_handler(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
struct net_device *dev = cdev->net;
|
||||
u32 ir;
|
||||
int ret;
|
||||
|
||||
if (pm_runtime_suspended(cdev->dev)) {
|
||||
m_can_coalescing_disable(cdev);
|
||||
if (pm_runtime_suspended(cdev->dev))
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ir = m_can_read(cdev, M_CAN_IR);
|
||||
m_can_coalescing_update(cdev, ir);
|
||||
@ -1250,11 +1235,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
m_can_disable_all_interrupts(cdev);
|
||||
napi_schedule(&cdev->napi);
|
||||
} else {
|
||||
int pkts;
|
||||
|
||||
pkts = m_can_rx_peripheral(dev, ir);
|
||||
if (pkts < 0)
|
||||
goto out_fail;
|
||||
ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1272,8 +1255,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
} else {
|
||||
if (ir & (IR_TEFN | IR_TEFW)) {
|
||||
/* New TX FIFO Element arrived */
|
||||
if (m_can_echo_tx_event(dev) != 0)
|
||||
goto out_fail;
|
||||
ret = m_can_echo_tx_event(dev);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1281,16 +1265,31 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
can_rx_offload_threaded_irq_finish(&cdev->offload);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
out_fail:
|
||||
m_can_disable_all_interrupts(cdev);
|
||||
return IRQ_HANDLED;
|
||||
static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = m_can_interrupt_handler(cdev);
|
||||
if (ret < 0) {
|
||||
m_can_disable_all_interrupts(cdev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart m_can_coalescing_timer(struct hrtimer *timer)
|
||||
{
|
||||
struct m_can_classdev *cdev = container_of(timer, struct m_can_classdev, hrtimer);
|
||||
|
||||
if (cdev->can.state == CAN_STATE_BUS_OFF ||
|
||||
cdev->can.state == CAN_STATE_STOPPED)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
irq_wake_thread(cdev->net->irq, cdev->net);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
@ -1542,6 +1541,7 @@ static int m_can_chip_config(struct net_device *dev)
|
||||
else
|
||||
interrupts &= ~(IR_ERR_LEC_31X);
|
||||
}
|
||||
cdev->active_interrupts = 0;
|
||||
m_can_interrupt_enable(cdev, interrupts);
|
||||
|
||||
/* route all interrupts to INT0 */
|
||||
@ -1991,8 +1991,17 @@ static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
|
||||
{
|
||||
struct m_can_classdev *cdev = container_of(timer, struct
|
||||
m_can_classdev, hrtimer);
|
||||
int ret;
|
||||
|
||||
m_can_isr(0, cdev->net);
|
||||
if (cdev->can.state == CAN_STATE_BUS_OFF ||
|
||||
cdev->can.state == CAN_STATE_STOPPED)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
ret = m_can_interrupt_handler(cdev);
|
||||
|
||||
/* On error or if napi is scheduled to read, stop the timer */
|
||||
if (ret < 0 || napi_is_scheduled(&cdev->napi))
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
hrtimer_forward_now(timer, ms_to_ktime(HRTIMER_POLL_INTERVAL_MS));
|
||||
|
||||
@ -2175,7 +2184,7 @@ static int m_can_set_coalesce(struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops m_can_ethtool_ops = {
|
||||
static const struct ethtool_ops m_can_ethtool_ops_coalescing = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
|
||||
ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
|
||||
ETHTOOL_COALESCE_TX_USECS_IRQ |
|
||||
@ -2186,18 +2195,20 @@ static const struct ethtool_ops m_can_ethtool_ops = {
|
||||
.set_coalesce = m_can_set_coalesce,
|
||||
};
|
||||
|
||||
static const struct ethtool_ops m_can_ethtool_ops_polling = {
|
||||
static const struct ethtool_ops m_can_ethtool_ops = {
|
||||
.get_ts_info = ethtool_op_get_ts_info,
|
||||
};
|
||||
|
||||
static int register_m_can_dev(struct net_device *dev)
|
||||
static int register_m_can_dev(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct net_device *dev = cdev->net;
|
||||
|
||||
dev->flags |= IFF_ECHO; /* we support local echo */
|
||||
dev->netdev_ops = &m_can_netdev_ops;
|
||||
if (dev->irq)
|
||||
dev->ethtool_ops = &m_can_ethtool_ops;
|
||||
if (dev->irq && cdev->is_peripheral)
|
||||
dev->ethtool_ops = &m_can_ethtool_ops_coalescing;
|
||||
else
|
||||
dev->ethtool_ops = &m_can_ethtool_ops_polling;
|
||||
dev->ethtool_ops = &m_can_ethtool_ops;
|
||||
|
||||
return register_candev(dev);
|
||||
}
|
||||
@ -2383,7 +2394,7 @@ int m_can_class_register(struct m_can_classdev *cdev)
|
||||
if (ret)
|
||||
goto rx_offload_del;
|
||||
|
||||
ret = register_m_can_dev(cdev->net);
|
||||
ret = register_m_can_dev(cdev);
|
||||
if (ret) {
|
||||
dev_err(cdev->dev, "registering %s failed (err=%d)\n",
|
||||
cdev->net->name, ret);
|
||||
@ -2430,12 +2441,15 @@ int m_can_class_suspend(struct device *dev)
|
||||
netif_device_detach(ndev);
|
||||
|
||||
/* leave the chip running with rx interrupt enabled if it is
|
||||
* used as a wake-up source.
|
||||
* used as a wake-up source. Coalescing needs to be reset then,
|
||||
* the timer is cancelled here, interrupts are done in resume.
|
||||
*/
|
||||
if (cdev->pm_wake_source)
|
||||
if (cdev->pm_wake_source) {
|
||||
hrtimer_cancel(&cdev->hrtimer);
|
||||
m_can_write(cdev, M_CAN_IE, IR_RF0N);
|
||||
else
|
||||
} else {
|
||||
m_can_stop(ndev);
|
||||
}
|
||||
|
||||
m_can_clk_stop(cdev);
|
||||
}
|
||||
@ -2465,6 +2479,13 @@ int m_can_class_resume(struct device *dev)
|
||||
return ret;
|
||||
|
||||
if (cdev->pm_wake_source) {
|
||||
/* Restore active interrupts but disable coalescing as
|
||||
* we may have missed important waterlevel interrupts
|
||||
* between suspend and resume. Timers are already
|
||||
* stopped in suspend. Here we enable all interrupts
|
||||
* again.
|
||||
*/
|
||||
cdev->active_interrupts |= IR_RF0N | IR_TEFN;
|
||||
m_can_write(cdev, M_CAN_IE, cdev->active_interrupts);
|
||||
} else {
|
||||
ret = m_can_start(ndev);
|
||||
|
Loading…
Reference in New Issue
Block a user