On 04.06.2024 15:15, 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. > > Reviewed-by: Jacob Keller <jacob.e.keller@xxxxxxxxx> > Signed-off-by: Diogo Ivo <diogo.ivo@xxxxxxxxxxx> > --- Reviewed-by: Wojciech Drewek <wojciech.drewek@xxxxxxxxx> > drivers/net/ethernet/ti/icssg/icss_iep.c | 74 ++++++++++++++++++++++++++++++++ > 1 file changed, 74 insertions(+) > > diff --git a/drivers/net/ethernet/ti/icssg/icss_iep.c b/drivers/net/ethernet/ti/icssg/icss_iep.c > index 3025e9c18970..b076be9c527c 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,57 @@ 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); > + const u32 *reg_offs = iep->plat_data->reg_offs; > + struct ptp_clock_event pevent; > + unsigned int val; > + u64 ns, ns_next; > + > + spin_lock(&iep->irq_lock); > + > + 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); > + > + 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; > + 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; > @@ -602,6 +655,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 +832,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 +874,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) > @@ -827,6 +885,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); >