diff mbox series

[2/3] net: ti: icss-iep: Enable compare events

Message ID 20240529-iep-v1-2-7273c07592d3@siemens.com (mailing list archive)
State New, archived
Headers show
Series Enable PTP timestamping/PPS for AM65x SR1.0 devices | expand

Commit Message

Diogo Ivo May 29, 2024, 4:05 p.m. UTC
The IEP module supports compare events, in which a value is written to a
hardware register and when the IEP counter reaches the written value an
interrupt is generated. Add handling for this interrupt in order to
support PPS events.

Signed-off-by: Diogo Ivo <diogo.ivo@siemens.com>
---
 drivers/net/ethernet/ti/icssg/icss_iep.c | 71 ++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

Comments

Jacob Keller May 30, 2024, 6:43 p.m. UTC | #1
On 5/29/2024 9:05 AM, Diogo Ivo wrote:
> The IEP module supports compare events, in which a value is written to a
> hardware register and when the IEP counter reaches the written value an
> interrupt is generated. Add handling for this interrupt in order to
> support PPS events.
> 
> Signed-off-by: Diogo Ivo <diogo.ivo@siemens.com>
> ---

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Sunil Kovvuri Goutham May 31, 2024, 5:12 a.m. UTC | #2
>-----Original Message-----
>From: Diogo Ivo <diogo.ivo@siemens.com>
>Sent: Wednesday, May 29, 2024 9:35 PM
>To: MD Danish Anwar <danishanwar@ti.com>; Roger Quadros
><rogerq@kernel.org>; David S. Miller <davem@davemloft.net>; Eric Dumazet
><edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
><pabeni@redhat.com>; Richard Cochran <richardcochran@gmail.com>;
>Nishanth Menon <nm@ti.com>; Vignesh Raghavendra <vigneshr@ti.com>;
>Tero Kristo <kristo@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof
>Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Jan
>Kiszka <jan.kiszka@siemens.com>
>Cc: linux-arm-kernel@lists.infradead.org; netdev@vger.kernel.org; linux-
>kernel@vger.kernel.org; devicetree@vger.kernel.org; Diogo Ivo
><diogo.ivo@siemens.com>
>Subject: [EXTERNAL] [PATCH 2/3] net: ti: icss-iep: Enable compare events
>
>The IEP module supports compare events, in which a value is written to a
>hardware register and when the IEP counter reaches the written value an
>interrupt is generated. Add handling for this interrupt in order to support PPS
>events.
>
>Signed-off-by: Diogo Ivo <diogo.ivo@siemens.com>
>---
> 	iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);
> 	if (!iep)
>@@ -827,6 +883,21 @@ static int icss_iep_probe(struct platform_device
>*pdev)
> 	if (IS_ERR(iep->base))
> 		return -ENODEV;
>
>+	iep->cap_cmp_irq = platform_get_irq_byname_optional(pdev,
>"iep_cap_cmp");
>+	if (iep->cap_cmp_irq < 0) {
>+		if (iep->cap_cmp_irq == -EPROBE_DEFER)
>+			return iep->cap_cmp_irq;

This info is coming from DT, is PROBE_DIFFER error return value possible ?

>+		iep->cap_cmp_irq = 0;
>+	} else {
>+		ret = devm_request_irq(dev, iep->cap_cmp_irq,
>+				       icss_iep_cap_cmp_irq,
>IRQF_TRIGGER_HIGH,
>+				       "iep_cap_cmp", iep);
>+		if (ret)
>+			return dev_err_probe(iep->dev, ret,
>+					     "Request irq failed for cap_cmp\n");

Can't this driver live without this feature ?

>+		INIT_WORK(&iep->work, icss_iep_cap_cmp_work);
>+	}
>+
Andrew Lunn June 2, 2024, 3:19 p.m. UTC | #3
> >+	iep->cap_cmp_irq = platform_get_irq_byname_optional(pdev,
> >"iep_cap_cmp");
> >+	if (iep->cap_cmp_irq < 0) {
> >+		if (iep->cap_cmp_irq == -EPROBE_DEFER)
> >+			return iep->cap_cmp_irq;
> 
> This info is coming from DT, is PROBE_DIFFER error return value possible ?

static int __platform_get_irq_byname(struct platform_device *dev,
				     const char *name)
{
	struct resource *r;
	int ret;

	ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name);
	if (ret > 0 || ret == -EPROBE_DEFER)
		return ret;

This suggests it can happen.

	Andrew
Diogo Ivo June 3, 2024, 10:02 a.m. UTC | #4
Hi Sunil,

On 5/31/24 6:12 AM, Sunil Kovvuri Goutham wrote:
>> From: Diogo Ivo <diogo.ivo@siemens.com>
>> Sent: Wednesday, May 29, 2024 9:35 PM
>>
>> +	iep->cap_cmp_irq = platform_get_irq_byname_optional(pdev,
>> "iep_cap_cmp");
>> +	if (iep->cap_cmp_irq < 0) {
>> +		if (iep->cap_cmp_irq == -EPROBE_DEFER)
>> +			return iep->cap_cmp_irq;
> 
> This info is coming from DT, is PROBE_DIFFER error return value possible ?

 From my understanding -EPROBE_DEFER is a possible error code.
platform_get_irq_byname_optional() will eventually call of_irq_get()
where we get -EPROBE_DEFER if the IRQ domain still hasn't been
initialized.

>> +		iep->cap_cmp_irq = 0;
>> +	} else {
>> +		ret = devm_request_irq(dev, iep->cap_cmp_irq,
>> +				       icss_iep_cap_cmp_irq,
>> IRQF_TRIGGER_HIGH,
>> +				       "iep_cap_cmp", iep);
>> +		if (ret)
>> +			return dev_err_probe(iep->dev, ret,
>> +					     "Request irq failed for cap_cmp\n");
> 
> Can't this driver live without this feature ?

Yes it can! I'll rework this logic so that we do not fail to probe
here and simply continue without PPS event support.

Thank you for the review!

Best regards,
Diogo
diff mbox series

Patch

diff --git a/drivers/net/ethernet/ti/icssg/icss_iep.c b/drivers/net/ethernet/ti/icssg/icss_iep.c
index 3025e9c18970..8337508ce8f0 100644
--- a/drivers/net/ethernet/ti/icssg/icss_iep.c
+++ b/drivers/net/ethernet/ti/icssg/icss_iep.c
@@ -17,6 +17,7 @@ 
 #include <linux/timekeeping.h>
 #include <linux/interrupt.h>
 #include <linux/of_irq.h>
+#include <linux/workqueue.h>
 
 #include "icss_iep.h"
 
@@ -122,6 +123,7 @@  struct icss_iep {
 	int cap_cmp_irq;
 	u64 period;
 	u32 latch_enable;
+	struct work_struct work;
 };
 
 /**
@@ -571,6 +573,55 @@  static int icss_iep_perout_enable(struct icss_iep *iep,
 	return ret;
 }
 
+static void icss_iep_cap_cmp_work(struct work_struct *work)
+{
+	struct icss_iep *iep = container_of(work, struct icss_iep, work);
+	struct ptp_clock_event pevent;
+	unsigned int val;
+	u64 ns, ns_next;
+
+	spin_lock(&iep->irq_lock);
+
+	ns = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP1_REG0]);
+	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {
+		val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP1_REG1]);
+		ns |= (u64)val << 32;
+	}
+	/* set next event */
+	ns_next = ns + iep->period;
+	writel(lower_32_bits(ns_next),
+	       iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP1_REG0]);
+	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
+		writel(upper_32_bits(ns_next),
+		       iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP1_REG1]);
+
+	pevent.pps_times.ts_real = ns_to_timespec64(ns);
+	pevent.type = PTP_CLOCK_PPSUSR;
+	pevent.index = 0;
+	ptp_clock_event(iep->ptp_clock, &pevent);
+	dev_dbg(iep->dev, "IEP:pps ts: %llu next:%llu:\n", ns, ns_next);
+
+	spin_unlock(&iep->irq_lock);
+}
+
+static irqreturn_t icss_iep_cap_cmp_irq(int irq, void *dev_id)
+{
+	struct icss_iep *iep = (struct icss_iep *)dev_id;
+	unsigned int val;
+
+	val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP_STAT_REG]);
+	/* The driver only enables CMP1 */
+	if (val & BIT(1)) {
+		/* Clear the event */
+		writel(BIT(1), iep->base + iep->plat_data->reg_offs[ICSS_IEP_CMP_STAT_REG]);
+		if (iep->pps_enabled || iep->perout_enabled)
+			schedule_work(&iep->work);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
 static int icss_iep_pps_enable(struct icss_iep *iep, int on)
 {
 	struct ptp_clock_request rq;
@@ -602,6 +653,8 @@  static int icss_iep_pps_enable(struct icss_iep *iep, int on)
 		ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
 	} else {
 		ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
+		if (iep->cap_cmp_irq)
+			cancel_work_sync(&iep->work);
 	}
 
 	if (!ret)
@@ -777,6 +830,8 @@  int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
 	if (iep->ops && iep->ops->perout_enable) {
 		iep->ptp_info.n_per_out = 1;
 		iep->ptp_info.pps = 1;
+	} else if (iep->cap_cmp_irq) {
+		iep->ptp_info.pps = 1;
 	}
 
 	if (iep->ops && iep->ops->extts_enable)
@@ -817,6 +872,7 @@  static int icss_iep_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct icss_iep *iep;
 	struct clk *iep_clk;
+	int ret;
 
 	iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);
 	if (!iep)
@@ -827,6 +883,21 @@  static int icss_iep_probe(struct platform_device *pdev)
 	if (IS_ERR(iep->base))
 		return -ENODEV;
 
+	iep->cap_cmp_irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp");
+	if (iep->cap_cmp_irq < 0) {
+		if (iep->cap_cmp_irq == -EPROBE_DEFER)
+			return iep->cap_cmp_irq;
+		iep->cap_cmp_irq = 0;
+	} else {
+		ret = devm_request_irq(dev, iep->cap_cmp_irq,
+				       icss_iep_cap_cmp_irq, IRQF_TRIGGER_HIGH,
+				       "iep_cap_cmp", iep);
+		if (ret)
+			return dev_err_probe(iep->dev, ret,
+					     "Request irq failed for cap_cmp\n");
+		INIT_WORK(&iep->work, icss_iep_cap_cmp_work);
+	}
+
 	iep_clk = devm_clk_get(dev, NULL);
 	if (IS_ERR(iep_clk))
 		return PTR_ERR(iep_clk);