summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJudith Mendez <jm@ti.com>2023-05-30 17:48:20 -0500
committerHiago De Franco <hiago.franco@toradex.com>2023-06-16 18:24:42 +0200
commiteebf5918e7c7e39eeec8779b9572edf736f19451 (patch)
tree0ddacec5a901a81ccbd048bf748a153ae4e9ee18
parent12a61f35933af8326d8267335d425b159eef71f1 (diff)
can: m_can: Add hrtimer to generate software interrupt
Introduce timer polling method to MCAN since some SoCs may not have M_CAN interrupt routed to A53 Linux and do not have interrupt property in device tree M_CAN node. On AM62x SoC, MCANs on MCU domain do not have hardware interrupt routed to A53 Linux, instead they will use timer polling method. Add an hrtimer to MCAN class device. Each MCAN will have its own hrtimer instantiated if there is no hardware interrupt found in device tree M_CAN node. The timer will generate a software interrupt every 1 ms. In hrtimer callback, we check if there is a transaction pending by reading a register, then process by calling the isr if there is. Upstream-Status: Submitted [https://lore.kernel.org/all/20230530224820.303619-3-jm@ti.com/] Signed-off-by: Judith Mendez <jm@ti.com>
-rw-r--r--drivers/net/can/m_can/m_can.c34
-rw-r--r--drivers/net/can/m_can/m_can.h3
-rw-r--r--drivers/net/can/m_can/m_can_platform.c23
3 files changed, 55 insertions, 5 deletions
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 41422f8f7930..f39686483d8d 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -22,6 +22,7 @@
#include <linux/can/dev.h>
#include <linux/pinctrl/consumer.h>
#include <linux/phy/phy.h>
+#include <linux/hrtimer.h>
#include "m_can.h"
@@ -322,6 +323,9 @@ enum m_can_reg {
#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT
#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT)
+/* Hrtimer polling interval */
+#define HRTIMER_POLL_INTERVAL 1
+
static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg)
{
return cdev->ops->read_reg(cdev, reg);
@@ -1244,6 +1248,12 @@ static void m_can_start(struct net_device *dev)
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
m_can_enable_all_interrupts(cdev);
+
+ if (dev->irq == 0) {
+ dev_dbg(cdev->dev, "Start hrtimer\n");
+ hrtimer_start(&cdev->hrtimer, ms_to_ktime(HRTIMER_POLL_INTERVAL),
+ HRTIMER_MODE_REL_PINNED);
+ }
}
static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
@@ -1407,6 +1417,11 @@ static void m_can_stop(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
+ if (dev->irq == 0) {
+ dev_dbg(cdev->dev, "Stop hrtimer\n");
+ hrtimer_cancel(&cdev->hrtimer);
+ }
+
/* disable all interrupts */
m_can_disable_all_interrupts(cdev);
@@ -1625,6 +1640,18 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
+{
+ struct m_can_classdev *cdev = container_of(timer, struct
+ m_can_classdev, hrtimer);
+
+ m_can_isr(0, cdev->net);
+
+ hrtimer_forward_now(timer, ms_to_ktime(HRTIMER_POLL_INTERVAL));
+
+ return HRTIMER_RESTART;
+}
+
static int m_can_open(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
@@ -1660,10 +1687,10 @@ static int m_can_open(struct net_device *dev)
err = request_threaded_irq(dev->irq, NULL, m_can_isr,
IRQF_ONESHOT,
dev->name, dev);
- } else {
+ }
+ if (dev->irq > 0)
err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
dev);
- }
if (err < 0) {
netdev_err(dev, "failed to request interrupt\n");
@@ -1851,6 +1878,9 @@ int m_can_class_register(struct m_can_classdev *m_can_dev)
goto clk_disable;
}
+ if (m_can_dev->net->irq == 0)
+ m_can_dev->hrtimer.function = &hrtimer_callback;
+
devm_can_led_init(m_can_dev->net);
of_can_transceiver(m_can_dev->net);
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index 8cad1235afa0..7ed217b777db 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/freezer.h>
+#include <linux/hrtimer.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
@@ -98,6 +99,8 @@ struct m_can_classdev {
int is_peripheral;
struct mram_cfg mcfg[MRAM_CFG_NUM];
+
+ struct hrtimer hrtimer;
};
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index 45910fb5145c..ab8e056e2ee7 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -7,6 +7,7 @@
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
+#include <linux/hrtimer.h>
#include "m_can.h"
@@ -82,11 +83,27 @@ static int m_can_plat_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
addr = devm_ioremap_resource(&pdev->dev, res);
- irq = platform_get_irq_byname(pdev, "int0");
- if (IS_ERR(addr) || irq < 0) {
- ret = -EINVAL;
+ if (IS_ERR(addr)) {
+ ret = PTR_ERR(addr);
goto probe_fail;
}
+ if (device_property_present(mcan_class->dev, "interrupts") ||
+ device_property_present(mcan_class->dev, "interrupt-names")) {
+ irq = platform_get_irq_byname(pdev, "int0");
+ if (irq == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto probe_fail;
+ }
+ if (irq < 0) {
+ ret = -EINVAL;
+ goto probe_fail;
+ }
+ } else {
+ irq = 0;
+ dev_dbg(mcan_class->dev, "Polling enabled, initialize hrtimer");
+ hrtimer_init(&mcan_class->hrtimer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL_PINNED);
+ }
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");