diff options
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/m_can/m_can.c | 77 | ||||
-rw-r--r-- | drivers/net/can/m_can/m_can.h | 5 |
2 files changed, 76 insertions, 6 deletions
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index a01c9261331d..b76fcf5f3889 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -422,6 +422,25 @@ static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) } } +static void m_can_interrupt_enable(struct m_can_classdev *cdev, u32 interrupts) +{ + if (cdev->active_interrupts == interrupts) + return; + cdev->ops->write_reg(cdev, M_CAN_IE, interrupts); + cdev->active_interrupts = interrupts; +} + +static void m_can_coalescing_disable(struct m_can_classdev *cdev) +{ + u32 new_interrupts = cdev->active_interrupts | IR_RF0N; + + if (!cdev->net->irq) + return; + + hrtimer_cancel(&cdev->hrtimer); + m_can_interrupt_enable(cdev, new_interrupts); +} + static inline void m_can_enable_all_interrupts(struct m_can_classdev *cdev) { if (!cdev->net->irq) { @@ -437,7 +456,9 @@ static inline void m_can_enable_all_interrupts(struct m_can_classdev *cdev) 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"); @@ -1091,15 +1112,42 @@ static int m_can_echo_tx_event(struct net_device *dev) return err; } +static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir) +{ + u32 new_interrupts = cdev->active_interrupts; + bool enable_timer = false; + + if (!cdev->net->irq) + return; + + if (cdev->rx_coalesce_usecs_irq > 0 && (ir & (IR_RF0N | IR_RF0W))) { + enable_timer = true; + new_interrupts &= ~IR_RF0N; + } else if (!hrtimer_active(&cdev->hrtimer)) { + new_interrupts |= IR_RF0N; + } + + m_can_interrupt_enable(cdev, new_interrupts); + if (enable_timer) { + hrtimer_start(&cdev->hrtimer, + ns_to_ktime(cdev->rx_coalesce_usecs_irq * NSEC_PER_USEC), + HRTIMER_MODE_REL); + } +} + 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); u32 ir; - if (pm_runtime_suspended(cdev->dev)) + if (pm_runtime_suspended(cdev->dev)) { + m_can_coalescing_disable(cdev); return IRQ_NONE; + } + ir = m_can_read(cdev, M_CAN_IR); + m_can_coalescing_update(cdev, ir); if (!ir) return IRQ_NONE; @@ -1114,13 +1162,17 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) * - state change IRQ * - bus error IRQ and bus error reporting */ - if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) { + if (ir & (IR_RF0N | IR_RF0W | IR_ERR_ALL_30X)) { cdev->irqstatus = ir; if (!cdev->is_peripheral) { m_can_disable_all_interrupts(cdev); napi_schedule(&cdev->napi); - } else if (m_can_rx_peripheral(dev, ir) < 0) { - goto out_fail; + } else { + int pkts; + + pkts = m_can_rx_peripheral(dev, ir); + if (pkts < 0) + goto out_fail; } } @@ -1156,6 +1208,15 @@ out_fail: return IRQ_HANDLED; } +static enum hrtimer_restart m_can_coalescing_timer(struct hrtimer *timer) +{ + struct m_can_classdev *cdev = container_of(timer, struct m_can_classdev, hrtimer); + + irq_wake_thread(cdev->net->irq, cdev->net); + + return HRTIMER_NORESTART; +} + static const struct can_bittiming_const m_can_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ @@ -1296,7 +1357,7 @@ static int m_can_chip_config(struct net_device *dev) /* Disable unused interrupts */ interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TEFW | IR_TFE | IR_TCF | IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | - IR_RF0F | IR_RF0W); + IR_RF0F); m_can_config_endisable(cdev, true); @@ -1340,6 +1401,7 @@ static int m_can_chip_config(struct net_device *dev) /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(cdev, M_CAN_RXF0C, + FIELD_PREP(RXFC_FWM_MASK, cdev->rx_max_coalesced_frames_irq) | FIELD_PREP(RXFC_FS_MASK, cdev->mcfg[MRAM_RXF0].num) | cdev->mcfg[MRAM_RXF0].off); @@ -1398,7 +1460,7 @@ static int m_can_chip_config(struct net_device *dev) else interrupts &= ~(IR_ERR_LEC_31X); } - m_can_write(cdev, M_CAN_IE, interrupts); + m_can_interrupt_enable(cdev, interrupts); /* route all interrupts to INT0 */ m_can_write(cdev, M_CAN_ILS, ILS_ALL_INT0); @@ -2082,6 +2144,9 @@ int m_can_class_register(struct m_can_classdev *cdev) hrtimer_init(&cdev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); cdev->hrtimer.function = &hrtimer_callback; + } else { + hrtimer_init(&cdev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cdev->hrtimer.function = m_can_coalescing_timer; } ret = m_can_dev_setup(cdev); diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 520e14277dff..b916206199f1 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -92,6 +92,11 @@ struct m_can_classdev { int pm_clock_support; int is_peripheral; + // Cached M_CAN_IE register content + u32 active_interrupts; + u32 rx_max_coalesced_frames_irq; + u32 rx_coalesce_usecs_irq; + struct mram_cfg mcfg[MRAM_CFG_NUM]; struct hrtimer hrtimer; |