diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 2107fa8ee32c..8f3efdf6f2bb 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -53,12 +53,18 @@ #define NETC_TMR_CUR_TIME_H 0x00f4 #define NETC_TMR_REGS_BAR 0 +#define NETC_GLOBAL_OFFSET 0x10000 +#define NETC_GLOBAL_IPBRR0 0xbf8 +#define IPBRR0_IP_REV GENMASK(15, 0) +#define NETC_REV_4_1 0x0401 #define NETC_TMR_FIPER_NUM 3 +#define NETC_TMR_INVALID_CHANNEL NETC_TMR_FIPER_NUM #define NETC_TMR_DEFAULT_PRSC 2 #define NETC_TMR_DEFAULT_ALARM GENMASK_ULL(63, 0) #define NETC_TMR_DEFAULT_FIPER GENMASK(31, 0) #define NETC_TMR_FIPER_MAX_PW GENMASK(4, 0) +#define NETC_TMR_ALARM_NUM 2 /* 1588 timer reference clock source select */ #define NETC_TMR_CCM_TIMER1 0 /* enet_timer1_clk_root, from CCM */ @@ -67,6 +73,19 @@ #define NETC_TMR_SYSCLK_333M 333333333U +enum netc_pp_type { + NETC_PP_PPS = 1, + NETC_PP_PEROUT, +}; + +struct netc_pp { + enum netc_pp_type type; + bool enabled; + int alarm_id; + u32 period; /* pulse period, ns */ + u64 stime; /* start time, ns */ +}; + struct netc_timer { void __iomem *base; struct pci_dev *pdev; @@ -82,8 +101,12 @@ struct netc_timer { int irq; char irq_name[24]; + int revision; u32 tmr_emask; - bool pps_enabled; + u8 pps_channel; + u8 fs_alarm_num; + u8 fs_alarm_bitmap; + struct netc_pp pp[NETC_TMR_FIPER_NUM]; /* periodic pulse */ }; #define netc_timer_rd(p, o) netc_read((p)->base + (o)) @@ -193,6 +216,7 @@ static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv, static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel, u32 integral_period) { + struct netc_pp *pp = &priv->pp[channel]; u64 alarm; /* Get the alarm value */ @@ -200,7 +224,116 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel, alarm = roundup_u64(alarm, NSEC_PER_SEC); alarm = roundup_u64(alarm, integral_period); - netc_timer_alarm_write(priv, alarm, 0); + netc_timer_alarm_write(priv, alarm, pp->alarm_id); +} + +static void netc_timer_set_perout_alarm(struct netc_timer *priv, int channel, + u32 integral_period) +{ + u64 cur_time = netc_timer_cur_time_read(priv); + struct netc_pp *pp = &priv->pp[channel]; + u64 alarm, delta, min_time; + u32 period = pp->period; + u64 stime = pp->stime; + + min_time = cur_time + NSEC_PER_MSEC + period; + if (stime < min_time) { + delta = min_time - stime; + stime += roundup_u64(delta, period); + } + + alarm = roundup_u64(stime - period, integral_period); + netc_timer_alarm_write(priv, alarm, pp->alarm_id); +} + +static int netc_timer_get_alarm_id(struct netc_timer *priv) +{ + int i; + + for (i = 0; i < priv->fs_alarm_num; i++) { + if (!(priv->fs_alarm_bitmap & BIT(i))) { + priv->fs_alarm_bitmap |= BIT(i); + break; + } + } + + return i; +} + +static u64 netc_timer_get_gclk_period(struct netc_timer *priv) +{ + /* TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz. + * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq. + * TMR_GCLK_period = (NSEC_PER_SEC * oclk_prsc) / clk_freq + */ + + return div_u64(mul_u32_u32(NSEC_PER_SEC, priv->oclk_prsc), + priv->clk_freq); +} + +static void netc_timer_enable_periodic_pulse(struct netc_timer *priv, + u8 channel) +{ + u32 fiper_pw, fiper, fiper_ctrl, integral_period; + struct netc_pp *pp = &priv->pp[channel]; + int alarm_id = pp->alarm_id; + + integral_period = netc_timer_get_integral_period(priv); + /* Set to desired FIPER interval in ns - TCLK_PERIOD */ + fiper = pp->period - integral_period; + fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper); + + fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); + fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) | + FIPER_CTRL_FS_ALARM(channel)); + fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw); + fiper_ctrl |= alarm_id ? FIPER_CTRL_FS_ALARM(channel) : 0; + + priv->tmr_emask |= TMR_TEVNET_PPEN(channel) | + TMR_TEVENT_ALMEN(alarm_id); + + if (pp->type == NETC_PP_PPS) + netc_timer_set_pps_alarm(priv, channel, integral_period); + else + netc_timer_set_perout_alarm(priv, channel, integral_period); + + netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask); + netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper); + netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); +} + +static void netc_timer_disable_periodic_pulse(struct netc_timer *priv, + u8 channel) +{ + struct netc_pp *pp = &priv->pp[channel]; + int alarm_id = pp->alarm_id; + u32 fiper_ctrl; + + if (!pp->enabled) + return; + + priv->tmr_emask &= ~(TMR_TEVNET_PPEN(channel) | + TMR_TEVENT_ALMEN(alarm_id)); + + fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); + fiper_ctrl |= FIPER_CTRL_DIS(channel); + + netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, alarm_id); + netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask); + netc_timer_wr(priv, NETC_TMR_FIPER(channel), NETC_TMR_DEFAULT_FIPER); + netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); +} + +static u8 netc_timer_select_pps_channel(struct netc_timer *priv) +{ + int i; + + for (i = 0; i < NETC_TMR_FIPER_NUM; i++) { + if (!priv->pp[i].enabled) + return i; + } + + return NETC_TMR_INVALID_CHANNEL; } /* Note that users should not use this API to output PPS signal on @@ -211,77 +344,178 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel, static int netc_timer_enable_pps(struct netc_timer *priv, struct ptp_clock_request *rq, int on) { - u32 fiper, fiper_ctrl; + struct device *dev = &priv->pdev->dev; unsigned long flags; + struct netc_pp *pp; + int err = 0; spin_lock_irqsave(&priv->lock, flags); - fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); - if (on) { - u32 integral_period, fiper_pw; + int alarm_id; + u8 channel; - if (priv->pps_enabled) + if (priv->pps_channel < NETC_TMR_FIPER_NUM) { + channel = priv->pps_channel; + } else { + channel = netc_timer_select_pps_channel(priv); + if (channel == NETC_TMR_INVALID_CHANNEL) { + dev_err(dev, "No available FIPERs\n"); + err = -EBUSY; + goto unlock_spinlock; + } + } + + pp = &priv->pp[channel]; + if (pp->enabled) goto unlock_spinlock; - integral_period = netc_timer_get_integral_period(priv); - fiper = NSEC_PER_SEC - integral_period; - fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper); - fiper_ctrl &= ~(FIPER_CTRL_DIS(0) | FIPER_CTRL_PW(0) | - FIPER_CTRL_FS_ALARM(0)); - fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw); - priv->tmr_emask |= TMR_TEVNET_PPEN(0) | TMR_TEVENT_ALMEN(0); - priv->pps_enabled = true; - netc_timer_set_pps_alarm(priv, 0, integral_period); + alarm_id = netc_timer_get_alarm_id(priv); + if (alarm_id == priv->fs_alarm_num) { + dev_err(dev, "No available ALARMs\n"); + err = -EBUSY; + goto unlock_spinlock; + } + + pp->enabled = true; + pp->type = NETC_PP_PPS; + pp->alarm_id = alarm_id; + pp->period = NSEC_PER_SEC; + priv->pps_channel = channel; + + netc_timer_enable_periodic_pulse(priv, channel); } else { - if (!priv->pps_enabled) + /* pps_channel is invalid if PPS is not enabled, so no + * processing is needed. + */ + if (priv->pps_channel >= NETC_TMR_FIPER_NUM) goto unlock_spinlock; - fiper = NETC_TMR_DEFAULT_FIPER; - priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) | - TMR_TEVENT_ALMEN(0)); - fiper_ctrl |= FIPER_CTRL_DIS(0); - priv->pps_enabled = false; - netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); + netc_timer_disable_periodic_pulse(priv, priv->pps_channel); + pp = &priv->pp[priv->pps_channel]; + priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id); + memset(pp, 0, sizeof(*pp)); + priv->pps_channel = NETC_TMR_INVALID_CHANNEL; } - netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask); - netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper); - netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); - unlock_spinlock: spin_unlock_irqrestore(&priv->lock, flags); - return 0; + return err; } -static void netc_timer_disable_pps_fiper(struct netc_timer *priv) +static int net_timer_enable_perout(struct netc_timer *priv, + struct ptp_clock_request *rq, int on) { - u32 fiper_ctrl; + struct device *dev = &priv->pdev->dev; + u32 channel = rq->perout.index; + unsigned long flags; + struct netc_pp *pp; + int err = 0; - if (!priv->pps_enabled) - return; + spin_lock_irqsave(&priv->lock, flags); + + pp = &priv->pp[channel]; + if (pp->type == NETC_PP_PPS) { + dev_err(dev, "FIPER%u is being used for PPS\n", channel); + err = -EBUSY; + goto unlock_spinlock; + } + + if (on) { + u64 period_ns, gclk_period, max_period, min_period; + struct timespec64 period, stime; + u32 integral_period; + int alarm_id; + + period.tv_sec = rq->perout.period.sec; + period.tv_nsec = rq->perout.period.nsec; + period_ns = timespec64_to_ns(&period); + + integral_period = netc_timer_get_integral_period(priv); + max_period = (u64)NETC_TMR_DEFAULT_FIPER + integral_period; + gclk_period = netc_timer_get_gclk_period(priv); + min_period = gclk_period * 4 + integral_period; + if (period_ns > max_period || period_ns < min_period) { + dev_err(dev, "The period range is %llu ~ %llu\n", + min_period, max_period); + err = -EINVAL; + goto unlock_spinlock; + } + + if (pp->enabled) { + alarm_id = pp->alarm_id; + } else { + alarm_id = netc_timer_get_alarm_id(priv); + if (alarm_id == priv->fs_alarm_num) { + dev_err(dev, "No available ALARMs\n"); + err = -EBUSY; + goto unlock_spinlock; + } + + pp->type = NETC_PP_PEROUT; + pp->enabled = true; + pp->alarm_id = alarm_id; + } + + stime.tv_sec = rq->perout.start.sec; + stime.tv_nsec = rq->perout.start.nsec; + pp->stime = timespec64_to_ns(&stime); + pp->period = period_ns; + + netc_timer_enable_periodic_pulse(priv, channel); + } else { + netc_timer_disable_periodic_pulse(priv, channel); + priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id); + memset(pp, 0, sizeof(*pp)); + } + +unlock_spinlock: + spin_unlock_irqrestore(&priv->lock, flags); + + return err; +} + +static void netc_timer_disable_fiper(struct netc_timer *priv) +{ + u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); + int i; + + for (i = 0; i < NETC_TMR_FIPER_NUM; i++) { + if (!priv->pp[i].enabled) + continue; + + fiper_ctrl |= FIPER_CTRL_DIS(i); + netc_timer_wr(priv, NETC_TMR_FIPER(i), NETC_TMR_DEFAULT_FIPER); + } - fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); - fiper_ctrl |= FIPER_CTRL_DIS(0); - netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_FIPER); netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); } -static void netc_timer_enable_pps_fiper(struct netc_timer *priv) +static void netc_timer_enable_fiper(struct netc_timer *priv) { - u32 fiper_ctrl, integral_period, fiper; + u32 integral_period = netc_timer_get_integral_period(priv); + u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); + int i; - if (!priv->pps_enabled) - return; + for (i = 0; i < NETC_TMR_FIPER_NUM; i++) { + struct netc_pp *pp = &priv->pp[i]; + u32 fiper; - integral_period = netc_timer_get_integral_period(priv); - fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); - fiper_ctrl &= ~FIPER_CTRL_DIS(0); - fiper = NSEC_PER_SEC - integral_period; + if (!pp->enabled) + continue; + + fiper_ctrl &= ~FIPER_CTRL_DIS(i); + + if (pp->type == NETC_PP_PPS) + netc_timer_set_pps_alarm(priv, i, integral_period); + else if (pp->type == NETC_PP_PEROUT) + netc_timer_set_perout_alarm(priv, i, integral_period); + + fiper = pp->period - integral_period; + netc_timer_wr(priv, NETC_TMR_FIPER(i), fiper); + } - netc_timer_set_pps_alarm(priv, 0, integral_period); - netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper); netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); } @@ -293,6 +527,8 @@ static int netc_timer_enable(struct ptp_clock_info *ptp, switch (rq->type) { case PTP_CLK_REQ_PPS: return netc_timer_enable_pps(priv, rq, on); + case PTP_CLK_REQ_PEROUT: + return net_timer_enable_perout(priv, rq, on); default: return -EOPNOTSUPP; } @@ -311,9 +547,9 @@ static void netc_timer_adjust_period(struct netc_timer *priv, u64 period) tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period, TMR_CTRL_TCLK_PERIOD); if (tmr_ctrl != old_tmr_ctrl) { - netc_timer_disable_pps_fiper(priv); + netc_timer_disable_fiper(priv); netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl); - netc_timer_enable_pps_fiper(priv); + netc_timer_enable_fiper(priv); } netc_timer_wr(priv, NETC_TMR_ADD, fractional_period); @@ -340,7 +576,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta) spin_lock_irqsave(&priv->lock, flags); - netc_timer_disable_pps_fiper(priv); + netc_timer_disable_fiper(priv); /* Adjusting TMROFF instead of TMR_CNT is that the timer * counter keeps increasing during reading and writing @@ -350,7 +586,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta) tmr_off += delta; netc_timer_offset_write(priv, tmr_off); - netc_timer_enable_pps_fiper(priv); + netc_timer_enable_fiper(priv); spin_unlock_irqrestore(&priv->lock, flags); @@ -387,10 +623,10 @@ static int netc_timer_settime64(struct ptp_clock_info *ptp, spin_lock_irqsave(&priv->lock, flags); - netc_timer_disable_pps_fiper(priv); + netc_timer_disable_fiper(priv); netc_timer_offset_write(priv, 0); netc_timer_cnt_write(priv, ns); - netc_timer_enable_pps_fiper(priv); + netc_timer_enable_fiper(priv); spin_unlock_irqrestore(&priv->lock, flags); @@ -404,6 +640,7 @@ static const struct ptp_clock_info netc_timer_ptp_caps = { .n_pins = 0, .n_alarm = 2, .pps = 1, + .n_per_out = 3, .adjfine = netc_timer_adjfine, .adjtime = netc_timer_adjtime, .gettimex64 = netc_timer_gettimex64, @@ -561,6 +798,9 @@ static irqreturn_t netc_timer_isr(int irq, void *data) if (tmr_event & TMR_TEVENT_ALMEN(0)) netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); + if (tmr_event & TMR_TEVENT_ALMEN(1)) + netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1); + if (tmr_event & TMR_TEVENT_PPEN_ALL) { event.type = PTP_CLOCK_PPS; ptp_clock_event(priv->clock, &event); @@ -604,6 +844,15 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv) pci_free_irq_vectors(pdev); } +static int netc_timer_get_global_ip_rev(struct netc_timer *priv) +{ + u32 val; + + val = netc_timer_rd(priv, NETC_GLOBAL_OFFSET + NETC_GLOBAL_IPBRR0); + + return val & IPBRR0_IP_REV; +} + static int netc_timer_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -616,12 +865,19 @@ static int netc_timer_probe(struct pci_dev *pdev, return err; priv = pci_get_drvdata(pdev); + priv->revision = netc_timer_get_global_ip_rev(priv); + if (priv->revision == NETC_REV_4_1) + priv->fs_alarm_num = 1; + else + priv->fs_alarm_num = NETC_TMR_ALARM_NUM; + err = netc_timer_parse_dt(priv); if (err) goto timer_pci_remove; priv->caps = netc_timer_ptp_caps; priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC; + priv->pps_channel = NETC_TMR_INVALID_CHANNEL; spin_lock_init(&priv->lock); snprintf(priv->irq_name, sizeof(priv->irq_name), "ptp-netc %s", pci_name(pdev));