Skip to content

Commit

Permalink
net: ethernet: ti: icss_iep: fix pps irq race vs pps disable
Browse files Browse the repository at this point in the history
When PPS is disabled the PPS IRQ can be running and PPS timer started which
could left IEP HW in undefined, partially configured state.

Add spin_lock to protect PPS disabling vs PPS IRQ and ensure PPS timer
canceled.

Signed-off-by: Grygorii Strashko <[email protected]>
Signed-off-by: Vignesh Raghavendra <[email protected]>
  • Loading branch information
grygoriyS authored and r-vignesh committed May 3, 2021
1 parent 5ecc4dc commit 5d58df8
Showing 1 changed file with 22 additions and 1 deletion.
23 changes: 22 additions & 1 deletion drivers/net/ethernet/ti/icss_iep.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ struct icss_iep {
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
struct mutex ptp_clk_mutex; /* PHC access serializer */
spinlock_t irq_lock; /* CMP IRQ vs icss_iep_ptp_enable access */
u32 def_inc;
s16 slow_cmp_inc;
u32 slow_cmp_count;
Expand Down Expand Up @@ -497,6 +498,7 @@ static int icss_iep_perout_enable_hw(struct icss_iep *iep,
static int icss_iep_perout_enable(struct icss_iep *iep,
struct ptp_perout_request *req, int on)
{
unsigned long flags;
int ret = 0;

mutex_lock(&iep->ptp_clk_mutex);
Expand All @@ -509,9 +511,12 @@ static int icss_iep_perout_enable(struct icss_iep *iep,
if (iep->perout_enabled == !!on)
goto exit;

spin_lock_irqsave(&iep->irq_lock, flags);
hrtimer_cancel(&iep->sync_timer);
ret = icss_iep_perout_enable_hw(iep, req, on);
if (!ret)
iep->perout_enabled = !!on;
spin_unlock_irqrestore(&iep->irq_lock, flags);

exit:
mutex_unlock(&iep->ptp_clk_mutex);
Expand All @@ -525,11 +530,18 @@ static irqreturn_t icss_iep_cap_cmp_handler(int irq, void *dev_id)
unsigned int val, index = 0, i, sts;
struct ptp_clock_event pevent;
irqreturn_t ret = IRQ_NONE;
unsigned long flags;
u64 ns;

spin_lock_irqsave(&iep->irq_lock, flags);

regmap_read(iep->map, ICSS_IEP_CMP_STAT_REG, &val);
if (val & BIT(CMP_INDEX(index))) {
regmap_write(iep->map, ICSS_IEP_CMP_STAT_REG, BIT(CMP_INDEX(index)));

if (!iep->pps_enabled && !iep->perout_enabled)
goto do_latch;

regmap_read(iep->map, ICSS_IEP_CMP1_REG0, &val);
ns = val;
if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {
Expand All @@ -550,9 +562,10 @@ static irqreturn_t icss_iep_cap_cmp_handler(int irq, void *dev_id)
ret = IRQ_HANDLED;
}

do_latch:
regmap_read(iep->map, ICSS_IEP_CAPTURE_STAT_REG, &sts);
if (!sts)
return ret;
goto cap_cmp_exit;

for (i = 0; i < iep->ptp_info.n_ext_ts; i++) {
if (sts & IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(i * 2)) {
Expand All @@ -571,6 +584,8 @@ static irqreturn_t icss_iep_cap_cmp_handler(int irq, void *dev_id)
}
}

cap_cmp_exit:
spin_unlock_irqrestore(&iep->irq_lock, flags);
return ret;
}

Expand All @@ -579,6 +594,7 @@ static int icss_iep_pps_enable(struct icss_iep *iep, int on)
int ret = 0;
struct timespec64 ts;
struct ptp_clock_request rq;
unsigned long flags;
u64 ns;

mutex_lock(&iep->ptp_clk_mutex);
Expand All @@ -591,6 +607,8 @@ static int icss_iep_pps_enable(struct icss_iep *iep, int on)
if (iep->pps_enabled == !!on)
goto exit;

spin_lock_irqsave(&iep->irq_lock, flags);

rq.perout.index = 0;
if (on) {
ns = icss_iep_gettime(iep);
Expand All @@ -601,12 +619,15 @@ static int icss_iep_pps_enable(struct icss_iep *iep, int on)
rq.perout.start.nsec = 0;
ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
} else {
hrtimer_cancel(&iep->sync_timer);
ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
}

if (!ret)
iep->pps_enabled = !!on;

spin_unlock_irqrestore(&iep->irq_lock, flags);

exit:
mutex_unlock(&iep->ptp_clk_mutex);

Expand Down

0 comments on commit 5d58df8

Please sign in to comment.