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. 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>
This commit is contained in:
parent
5056860cf8
commit
f18ad402cd
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user