]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: ti: icss-iep: Enable compare events
authorDiogo Ivo <diogo.ivo@siemens.com>
Mon, 17 Jun 2024 15:21:43 +0000 (16:21 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 19 Jun 2024 09:56:10 +0000 (10:56 +0100)
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.

Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Diogo Ivo <diogo.ivo@siemens.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/icssg/icss_iep.c

index d52e42fa64f20ccf355bbbbe65beffe516539228..003668dee7386af437e1bf38ad2121e8b50e1c13 100644 (file)
@@ -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"
 
@@ -121,6 +122,7 @@ struct icss_iep {
        int cap_cmp_irq;
        u64 period;
        u32 latch_enable;
+       struct work_struct work;
 };
 
 /**
@@ -563,6 +565,57 @@ exit:
        return ret;
 }
 
+static void icss_iep_cap_cmp_work(struct work_struct *work)
+{
+       struct icss_iep *iep = container_of(work, struct icss_iep, work);
+       const u32 *reg_offs = iep->plat_data->reg_offs;
+       struct ptp_clock_event pevent;
+       unsigned int val;
+       u64 ns, ns_next;
+
+       mutex_lock(&iep->ptp_clk_mutex);
+
+       ns = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);
+       if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {
+               val = readl(iep->base + 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 + reg_offs[ICSS_IEP_CMP1_REG0]);
+       if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
+               writel(upper_32_bits(ns_next),
+                      iep->base + 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);
+
+       mutex_unlock(&iep->ptp_clk_mutex);
+}
+
+static irqreturn_t icss_iep_cap_cmp_irq(int irq, void *dev_id)
+{
+       struct icss_iep *iep = (struct icss_iep *)dev_id;
+       const u32 *reg_offs = iep->plat_data->reg_offs;
+       unsigned int val;
+
+       val = readl(iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);
+       /* The driver only enables CMP1 */
+       if (val & BIT(1)) {
+               /* Clear the event */
+               writel(BIT(1), iep->base + 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;
@@ -591,6 +644,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)
@@ -764,6 +819,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)
@@ -804,6 +861,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, irq;
 
        iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);
        if (!iep)
@@ -814,6 +872,22 @@ static int icss_iep_probe(struct platform_device *pdev)
        if (IS_ERR(iep->base))
                return -ENODEV;
 
+       irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp");
+       if (irq == -EPROBE_DEFER)
+               return irq;
+
+       if (irq > 0) {
+               ret = devm_request_irq(dev, irq, icss_iep_cap_cmp_irq,
+                                      IRQF_TRIGGER_HIGH, "iep_cap_cmp", iep);
+               if (ret) {
+                       dev_info(iep->dev, "cap_cmp irq request failed: %x\n",
+                                ret);
+               } else {
+                       iep->cap_cmp_irq = irq;
+                       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);