Add an hrtimer to MCAN class device. Each MCAN will have its own
hrtimer instantiated if there is no hardware interrupt found and
poll-interval property is defined in device tree M_CAN node.
The hrtimer 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.
Signed-off-by: Judith Mendez <jm@ti.com>
---
Changelog:
v6:
- Move hrtimer stop/start function calls to m_can_open and m_can_close to
support power suspend/resume
v5:
- Change dev_dbg to dev_info if hardware interrupt exists and polling
is enabled
v4:
- No changes
v3:
- Create a define for 1 ms polling interval
- Change plarform_get_irq to optional to not print error msg
v2:
- Add functionality to check for 'poll-interval' property in MCAN node
- Add 'polling' flag in driver to check if device is using polling method
- Check for timer polling and hardware interrupt cases, default to
hardware interrupt method
- Change ns_to_ktime() to ms_to_ktime()
---
drivers/net/can/m_can/m_can.c | 31 ++++++++++++++++++++++-
drivers/net/can/m_can/m_can.h | 4 +++
drivers/net/can/m_can/m_can_platform.c | 35 +++++++++++++++++++++++---
3 files changed, 66 insertions(+), 4 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index a5003435802b..cfb3e433c0dd 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/can/dev.h>
#include <linux/ethtool.h>
+#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -308,6 +309,9 @@ enum m_can_reg {
#define TX_EVENT_MM_MASK GENMASK(31, 24)
#define TX_EVENT_TXTS_MASK GENMASK(15, 0)
+/* Hrtimer polling interval */
+#define HRTIMER_POLL_INTERVAL 1
+
/* The ID and DLC registers are adjacent in M_CAN FIFO memory,
* and we can save a (potentially slow) bus round trip by combining
* reads and writes to them.
@@ -1414,6 +1418,12 @@ static int m_can_start(struct net_device *dev)
m_can_enable_all_interrupts(cdev);
+ if (cdev->polling) {
+ dev_dbg(cdev->dev, "Start hrtimer\n");
+ hrtimer_start(&cdev->hrtimer, ms_to_ktime(HRTIMER_POLL_INTERVAL),
+ HRTIMER_MODE_REL_PINNED);
+ }
+
return 0;
}
@@ -1571,6 +1581,11 @@ static void m_can_stop(struct net_device *dev)
/* disable all interrupts */
m_can_disable_all_interrupts(cdev);
+ if (cdev->polling) {
+ dev_dbg(cdev->dev, "Disabling the hrtimer\n");
+ hrtimer_cancel(&cdev->hrtimer);
+ }
+
/* Set init mode to disengage from the network */
m_can_config_endisable(cdev, true);
@@ -1793,6 +1808,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);
@@ -1831,9 +1858,11 @@ 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 {
+ } else if (!cdev->polling) {
err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
dev);
+ } else {
+ cdev->hrtimer.function = &hrtimer_callback;
}
if (err < 0) {
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index a839dc71dc9b..e9db5cce4e68 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/freezer.h>
+#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -93,6 +94,9 @@ struct m_can_classdev {
int is_peripheral;
struct mram_cfg mcfg[MRAM_CFG_NUM];
+
+ struct hrtimer hrtimer;
+ bool polling;
};
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev, int sizeof_priv);
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index 94dc82644113..3e60cebd9d12 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -5,6 +5,7 @@
//
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+#include <linux/hrtimer.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@@ -96,12 +97,40 @@ static int m_can_plat_probe(struct platform_device *pdev)
goto probe_fail;
addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
- 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;
}
+ irq = platform_get_irq_byname_optional(pdev, "int0");
+ if (irq == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto probe_fail;
+ }
+
+ if (device_property_present(mcan_class->dev, "interrupts") ||
+ device_property_present(mcan_class->dev, "interrupt-names"))
+ mcan_class->polling = false;
+ else
+ mcan_class->polling = true;
+
+ if (!mcan_class->polling && irq < 0) {
+ ret = -ENXIO;
+ dev_err_probe(mcan_class->dev, ret, "IRQ int0 not found, polling not activated\n");
+ goto probe_fail;
+ }
+
+ if (mcan_class->polling) {
+ if (irq > 0) {
+ mcan_class->polling = false;
+ dev_info(mcan_class->dev, "Polling enabled, using hardware IRQ\n");
+ } else {
+ 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");
if (!res) {
--
2.17.1
On 18.05.2023 14:36:13, Judith Mendez wrote:
> Add an hrtimer to MCAN class device. Each MCAN will have its own
> hrtimer instantiated if there is no hardware interrupt found and
> poll-interval property is defined in device tree M_CAN node.
>
> The hrtimer 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.
>
> Signed-off-by: Judith Mendez <jm@ti.com>
[...]
> diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
> index 94dc82644113..3e60cebd9d12 100644
> --- a/drivers/net/can/m_can/m_can_platform.c
> +++ b/drivers/net/can/m_can/m_can_platform.c
> @@ -5,6 +5,7 @@
> //
> // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>
> +#include <linux/hrtimer.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
>
> @@ -96,12 +97,40 @@ static int m_can_plat_probe(struct platform_device *pdev)
> goto probe_fail;
>
> addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
> - 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;
> }
>
As we don't use an explicit "poll-interval" anymore, this needs some
cleanup. The flow should be (pseudo code, error handling omitted):
if (device_property_present("interrupts") {
platform_get_irq_byname();
polling = false;
} else {
hrtimer_init();
polling = true;
}
> + irq = platform_get_irq_byname_optional(pdev, "int0");
Remove the "_optional" and....
> + if (irq == -EPROBE_DEFER) {
> + ret = -EPROBE_DEFER;
> + goto probe_fail;
> + }
> +
> + if (device_property_present(mcan_class->dev, "interrupts") ||
> + device_property_present(mcan_class->dev, "interrupt-names"))
> + mcan_class->polling = false;
...move the platform_get_irq_byname() here
> + else
> + mcan_class->polling = true;
> +
> + if (!mcan_class->polling && irq < 0) {
> + ret = -ENXIO;
> + dev_err_probe(mcan_class->dev, ret, "IRQ int0 not found, polling not activated\n");
> + goto probe_fail;
> + }
Remove this check.
> +
> + if (mcan_class->polling) {
> + if (irq > 0) {
> + mcan_class->polling = false;
> + dev_info(mcan_class->dev, "Polling enabled, using hardware IRQ\n");
Remove this.
> + } else {
> + dev_dbg(mcan_class->dev, "Polling enabled, initialize hrtimer");
> + hrtimer_init(&mcan_class->hrtimer, CLOCK_MONOTONIC,
> + HRTIMER_MODE_REL_PINNED);
move this backwards, where you set "polling = true"
> + }
> + }
> +
> /* message ram could be shared */
> res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
> if (!res) {
> --
> 2.17.1
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
Hello Marc,
On 5/19/23 2:16 AM, Marc Kleine-Budde wrote:
> On 18.05.2023 14:36:13, Judith Mendez wrote:
>> Add an hrtimer to MCAN class device. Each MCAN will have its own
>> hrtimer instantiated if there is no hardware interrupt found and
>> poll-interval property is defined in device tree M_CAN node.
>>
>> The hrtimer 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.
>>
>> Signed-off-by: Judith Mendez <jm@ti.com>
>
> [...]
Missed this poll-interval, thanks.
>
>> diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
>> index 94dc82644113..3e60cebd9d12 100644
>> --- a/drivers/net/can/m_can/m_can_platform.c
>> +++ b/drivers/net/can/m_can/m_can_platform.c
>> @@ -5,6 +5,7 @@
>> //
>> // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>>
>> +#include <linux/hrtimer.h>
>> #include <linux/phy/phy.h>
>> #include <linux/platform_device.h>
>>
>> @@ -96,12 +97,40 @@ static int m_can_plat_probe(struct platform_device *pdev)
>> goto probe_fail;
>>
>> addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
>> - 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;
>> }
>>
>
> As we don't use an explicit "poll-interval" anymore, this needs some
> cleanup. The flow should be (pseudo code, error handling omitted):
>
> if (device_property_present("interrupts") {
> platform_get_irq_byname();
> polling = false;
> } else {
> hrtimer_init();
> polling = true;
> }
Ok.
>
>> + irq = platform_get_irq_byname_optional(pdev, "int0");
>
> Remove the "_optional" and....
On V2, you asked to add the _optional?.....
> irq = platform_get_irq_byname(pdev, "int0");
use platform_get_irq_byname_optional(), it doesn't print an error
message.
>
>> + if (irq == -EPROBE_DEFER) {
>> + ret = -EPROBE_DEFER;
>> + goto probe_fail;
>> + }
>> +
>> + if (device_property_present(mcan_class->dev, "interrupts") ||
>> + device_property_present(mcan_class->dev, "interrupt-names"))
>> + mcan_class->polling = false;
>
> ...move the platform_get_irq_byname() here
ok,
>
>> + else
>> + mcan_class->polling = true;
>> +
>> + if (!mcan_class->polling && irq < 0) {
>> + ret = -ENXIO;
>> + dev_err_probe(mcan_class->dev, ret, "IRQ int0 not found, polling not activated\n");
>> + goto probe_fail;
>> + }
>
> Remove this check.
Should we not go to 'probe fail' if polling is not activated and irq is
not found?
>
>> +
>> + if (mcan_class->polling) {
>> + if (irq > 0) {
>> + mcan_class->polling = false;
>> + dev_info(mcan_class->dev, "Polling enabled, using hardware IRQ\n");
>
> Remove this.
Remove the dev_info?
>
>> + } else {
>> + dev_dbg(mcan_class->dev, "Polling enabled, initialize hrtimer");
>> + hrtimer_init(&mcan_class->hrtimer, CLOCK_MONOTONIC,
>> + HRTIMER_MODE_REL_PINNED);
>
> move this backwards, where you set "polling = true"
ok,
>
>> + }
>> + }
>> +
>> /* message ram could be shared */
>> res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
>> if (!res) {
>> --
>> 2.17.1
- judith
On 22.05.2023 10:17:38, Judith Mendez wrote:
> > > diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
> > > index 94dc82644113..3e60cebd9d12 100644
> > > --- a/drivers/net/can/m_can/m_can_platform.c
> > > +++ b/drivers/net/can/m_can/m_can_platform.c
> > > @@ -5,6 +5,7 @@
> > > //
> > > // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
> > > +#include <linux/hrtimer.h>
> > > #include <linux/phy/phy.h>
> > > #include <linux/platform_device.h>
> > > @@ -96,12 +97,40 @@ static int m_can_plat_probe(struct platform_device *pdev)
> > > goto probe_fail;
> > > addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
> > > - 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;
> > > }
> >
> > As we don't use an explicit "poll-interval" anymore, this needs some
> > cleanup. The flow should be (pseudo code, error handling omitted):
> >
> > if (device_property_present("interrupts") {
> > platform_get_irq_byname();
> > polling = false;
> > } else {
> > hrtimer_init();
> > polling = true;
> > }
>
> Ok.
>
> >
> > > + irq = platform_get_irq_byname_optional(pdev, "int0");
> >
> > Remove the "_optional" and....
>
> On V2, you asked to add the _optional?.....
>
> > irq = platform_get_irq_byname(pdev, "int0");
>
> use platform_get_irq_byname_optional(), it doesn't print an error
> message.
ACK - I said that back in v2, when there was "poll-interval". But now we
don't use "poll-interval" anymore, but test if interrupt properties are
present.
See again pseudo-code I posted in my last mail:
| if (device_property_present("interrupts") {
| platform_get_irq_byname();
If this throws an error, it's fatal, bail out.
| polling = false;
| } else {
| hrtimer_init();
| polling = true;
| }
>
> >
> > > + if (irq == -EPROBE_DEFER) {
> > > + ret = -EPROBE_DEFER;
> > > + goto probe_fail;
> > > + }
> > > +
> > > + if (device_property_present(mcan_class->dev, "interrupts") ||
> > > + device_property_present(mcan_class->dev, "interrupt-names"))
> > > + mcan_class->polling = false;
> >
> > ...move the platform_get_irq_byname() here
>
> ok,
>
> >
> > > + else
> > > + mcan_class->polling = true;
> > > +
> > > + if (!mcan_class->polling && irq < 0) {
> > > + ret = -ENXIO;
> > > + dev_err_probe(mcan_class->dev, ret, "IRQ int0 not found, polling not activated\n");
> > > + goto probe_fail;
> > > + }
> >
> > Remove this check.
>
> Should we not go to 'probe fail' if polling is not activated and irq is not
> found?
If an interrupt property is present in the DT, we use it - if request
IRQ fails, something is broken and we've already bailed out. See above.
If there is no interrupt property we use polling.
>
> >
> > > +
> > > + if (mcan_class->polling) {
> > > + if (irq > 0) {
> > > + mcan_class->polling = false;
> > > + dev_info(mcan_class->dev, "Polling enabled, using hardware IRQ\n");
> >
> > Remove this.
>
> Remove the dev_info?
ACK, this is not possible anymore - we cannot have polling enabled and
HW IRQs configured.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
Hello,
On 5/22/23 1:37 PM, Marc Kleine-Budde wrote:
> On 22.05.2023 10:17:38, Judith Mendez wrote:
>>>> diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
>>>> index 94dc82644113..3e60cebd9d12 100644
>>>> --- a/drivers/net/can/m_can/m_can_platform.c
>>>> +++ b/drivers/net/can/m_can/m_can_platform.c
>>>> @@ -5,6 +5,7 @@
>>>> //
>>>> // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>>>> +#include <linux/hrtimer.h>
>>>> #include <linux/phy/phy.h>
>>>> #include <linux/platform_device.h>
>>>> @@ -96,12 +97,40 @@ static int m_can_plat_probe(struct platform_device *pdev)
>>>> goto probe_fail;
>>>> addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
>>>> - 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;
>>>> }
>>>
>>> As we don't use an explicit "poll-interval" anymore, this needs some
>>> cleanup. The flow should be (pseudo code, error handling omitted):
>>>
>>> if (device_property_present("interrupts") {
>>> platform_get_irq_byname();
>>> polling = false;
>>> } else {
>>> hrtimer_init();
>>> polling = true;
>>> }
>>
>> Ok.
>>
>>>
>>>> + irq = platform_get_irq_byname_optional(pdev, "int0");
>>>
>>> Remove the "_optional" and....
>>
>> On V2, you asked to add the _optional?.....
>>
>>> irq = platform_get_irq_byname(pdev, "int0");
>>
>> use platform_get_irq_byname_optional(), it doesn't print an error
>> message.
>
> ACK - I said that back in v2, when there was "poll-interval". But now we
> don't use "poll-interval" anymore, but test if interrupt properties are
> present.
>
> See again pseudo-code I posted in my last mail:
>
> | if (device_property_present("interrupts") {
> | platform_get_irq_byname();
>
> If this throws an error, it's fatal, bail out.
>
> | polling = false;
> | } else {
> | hrtimer_init();
> | polling = true;
> | }
>
Ok, will add this then..
>>
>>>
>>>> + if (irq == -EPROBE_DEFER) {
>>>> + ret = -EPROBE_DEFER;
>>>> + goto probe_fail;
>>>> + }
>>>> +
>>>> + if (device_property_present(mcan_class->dev, "interrupts") ||
>>>> + device_property_present(mcan_class->dev, "interrupt-names"))
>>>> + mcan_class->polling = false;
>>>
>>> ...move the platform_get_irq_byname() here
>>
>> ok,
>>
>>>
>>>> + else
>>>> + mcan_class->polling = true;
>>>> +
>>>> + if (!mcan_class->polling && irq < 0) {
>>>> + ret = -ENXIO;
>>>> + dev_err_probe(mcan_class->dev, ret, "IRQ int0 not found, polling not activated\n");
>>>> + goto probe_fail;
>>>> + }
>>>
>>> Remove this check.
>>
>> Should we not go to 'probe fail' if polling is not activated and irq is not
>> found?
>
> If an interrupt property is present in the DT, we use it - if request
> IRQ fails, something is broken and we've already bailed out. See above.
> If there is no interrupt property we use polling.
Got it, thanks.
>>
>>>
>>>> +
>>>> + if (mcan_class->polling) {
>>>> + if (irq > 0) {
>>>> + mcan_class->polling = false;
>>>> + dev_info(mcan_class->dev, "Polling enabled, using hardware IRQ\n");
>>>
>>> Remove this.
>>
>> Remove the dev_info?
>
> ACK, this is not possible anymore - we cannot have polling enabled and
> HW IRQs configured.
Sounds good, will submit a v7 with these cleanup changes.
regards,
Judith
© 2016 - 2026 Red Hat, Inc.