Skip to content

Commit

Permalink
octeontx2-pf: Enable PTP PPS output support
Browse files Browse the repository at this point in the history
PTP block supports generating PPS output signal on GPIO pin. This patch
adds the support in the PTP PHC driver using standard periodic output
interface.

User can enable/disable/configure PPS by writing to the below sysfs entry

echo perout.index start.sec start.nsec period.sec period.nsec >
/sys/class/ptp/ptp0/period

Example to generate 50% duty cycle PPS signal:
echo 0 0 0 0 500000000 >  /sys/class/ptp/ptp0/period

Signed-off-by: Hariprasad Kelam <hkelam@marvell.com>
Signed-off-by: Sunil Kovvuri Goutham <sgoutham@marvell.com>
Signed-off-by: Sai Krishna <saikrishnag@marvell.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Hariprasad Kelam authored and David S. Miller committed Sep 15, 2023
1 parent e73d5fb commit 35293cb
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 37 deletions.
5 changes: 3 additions & 2 deletions drivers/net/ethernet/marvell/octeontx2/af/mbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -1574,7 +1574,7 @@ enum ptp_op {
PTP_OP_GET_CLOCK = 1,
PTP_OP_GET_TSTMP = 2,
PTP_OP_SET_THRESH = 3,
PTP_OP_EXTTS_ON = 4,
PTP_OP_PPS_ON = 4,
PTP_OP_ADJTIME = 5,
PTP_OP_SET_CLOCK = 6,
};
Expand All @@ -1584,7 +1584,8 @@ struct ptp_req {
u8 op;
s64 scaled_ppm;
u64 thresh;
int extts_on;
u64 period;
int pps_on;
s64 delta;
u64 clk;
};
Expand Down
86 changes: 59 additions & 27 deletions drivers/net/ethernet/marvell/octeontx2/af/ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#define PTP_PPS_HI_INCR 0xF60ULL
#define PTP_PPS_LO_INCR 0xF68ULL
#define PTP_PPS_THRESH_LO 0xF50ULL
#define PTP_PPS_THRESH_HI 0xF58ULL

#define PTP_CLOCK_LO 0xF08ULL
Expand Down Expand Up @@ -411,29 +412,12 @@ void ptp_start(struct rvu *rvu, u64 sclk, u32 ext_clk_freq, u32 extts)
}

clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
clock_cfg |= PTP_CLOCK_CFG_PPS_EN | PTP_CLOCK_CFG_PPS_INV;
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
clock_cfg &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK;
clock_cfg |= (ATOMIC_SET << 26);
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);

/* Set 50% duty cycle for 1Hz output */
writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_HI_INCR);
writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_LO_INCR);
if (cn10k_ptp_errata(ptp)) {
/* The ptp_clock_hi rollsover to zero once clock cycle before it
* reaches one second boundary. so, program the pps_lo_incr in
* such a way that the pps threshold value comparison at one
* second boundary will succeed and pps edge changes. After each
* one second boundary, the hrtimer handler will be invoked and
* reprograms the pps threshold value.
*/
ptp->clock_period = NSEC_PER_SEC / ptp->clock_rate;
writeq((0x1dcd6500ULL - ptp->clock_period) << 32,
ptp->reg_base + PTP_PPS_LO_INCR);
}

if (cn10k_ptp_errata(ptp))
clock_comp = ptp_calc_adjusted_comp(ptp->clock_rate);
else
Expand Down Expand Up @@ -465,20 +449,68 @@ static int ptp_set_thresh(struct ptp *ptp, u64 thresh)
return 0;
}

static int ptp_extts_on(struct ptp *ptp, int on)
static int ptp_config_hrtimer(struct ptp *ptp, int on)
{
u64 ptp_clock_hi;

if (cn10k_ptp_errata(ptp)) {
if (on) {
ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI);
ptp_hrtimer_start(ptp, (ktime_t)ptp_clock_hi);
} else {
if (hrtimer_active(&ptp->hrtimer))
hrtimer_cancel(&ptp->hrtimer);
if (on) {
ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI);
ptp_hrtimer_start(ptp, (ktime_t)ptp_clock_hi);
} else {
if (hrtimer_active(&ptp->hrtimer))
hrtimer_cancel(&ptp->hrtimer);
}

return 0;
}

static int ptp_pps_on(struct ptp *ptp, int on, u64 period)
{
u64 clock_cfg;

clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
if (on) {
if (cn10k_ptp_errata(ptp) && period != NSEC_PER_SEC) {
dev_err(&ptp->pdev->dev, "Supports max period value as 1 second\n");
return -EINVAL;
}

if (period > (8 * NSEC_PER_SEC)) {
dev_err(&ptp->pdev->dev, "Supports max period as 8 seconds\n");
return -EINVAL;
}

clock_cfg |= PTP_CLOCK_CFG_PPS_EN | PTP_CLOCK_CFG_PPS_INV;
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);

writeq(0, ptp->reg_base + PTP_PPS_THRESH_HI);
writeq(0, ptp->reg_base + PTP_PPS_THRESH_LO);

/* Configure high/low phase time */
period = period / 2;
writeq(((u64)period << 32), ptp->reg_base + PTP_PPS_HI_INCR);
writeq(((u64)period << 32), ptp->reg_base + PTP_PPS_LO_INCR);
} else {
clock_cfg &= ~(PTP_CLOCK_CFG_PPS_EN | PTP_CLOCK_CFG_PPS_INV);
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
}

if (on && cn10k_ptp_errata(ptp)) {
/* The ptp_clock_hi rollsover to zero once clock cycle before it
* reaches one second boundary. so, program the pps_lo_incr in
* such a way that the pps threshold value comparison at one
* second boundary will succeed and pps edge changes. After each
* one second boundary, the hrtimer handler will be invoked and
* reprograms the pps threshold value.
*/
ptp->clock_period = NSEC_PER_SEC / ptp->clock_rate;
writeq((0x1dcd6500ULL - ptp->clock_period) << 32,
ptp->reg_base + PTP_PPS_LO_INCR);
}

if (cn10k_ptp_errata(ptp))
ptp_config_hrtimer(ptp, on);

return 0;
}

Expand Down Expand Up @@ -613,8 +645,8 @@ int rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req,
case PTP_OP_SET_THRESH:
err = ptp_set_thresh(rvu->ptp, req->thresh);
break;
case PTP_OP_EXTTS_ON:
err = ptp_extts_on(rvu->ptp, req->extts_on);
case PTP_OP_PPS_ON:
err = ptp_pps_on(rvu->ptp, req->pps_on, req->period);
break;
case PTP_OP_ADJTIME:
ptp_atomic_adjtime(rvu->ptp, req->delta);
Expand Down
31 changes: 23 additions & 8 deletions drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ static int ptp_set_thresh(struct otx2_ptp *ptp, u64 thresh)
return otx2_sync_mbox_msg(&ptp->nic->mbox);
}

static int ptp_extts_on(struct otx2_ptp *ptp, int on)
static int ptp_pps_on(struct otx2_ptp *ptp, int on, u64 period)
{
struct ptp_req *req;

Expand All @@ -186,8 +186,9 @@ static int ptp_extts_on(struct otx2_ptp *ptp, int on)
if (!req)
return -ENOMEM;

req->op = PTP_OP_EXTTS_ON;
req->extts_on = on;
req->op = PTP_OP_PPS_ON;
req->pps_on = on;
req->period = period;

return otx2_sync_mbox_msg(&ptp->nic->mbox);
}
Expand Down Expand Up @@ -276,8 +277,8 @@ static int otx2_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
switch (func) {
case PTP_PF_NONE:
case PTP_PF_EXTTS:
break;
case PTP_PF_PEROUT:
break;
case PTP_PF_PHYSYNC:
return -1;
}
Expand Down Expand Up @@ -340,6 +341,7 @@ static int otx2_ptp_enable(struct ptp_clock_info *ptp_info,
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
u64 period = 0;
int pin;

if (!ptp->nic)
Expand All @@ -351,12 +353,24 @@ static int otx2_ptp_enable(struct ptp_clock_info *ptp_info,
rq->extts.index);
if (pin < 0)
return -EBUSY;
if (on) {
ptp_extts_on(ptp, on);
if (on)
schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200));
} else {
ptp_extts_on(ptp, on);
else
cancel_delayed_work_sync(&ptp->extts_work);

return 0;
case PTP_CLK_REQ_PEROUT:
if (rq->perout.flags)
return -EOPNOTSUPP;

if (rq->perout.index >= ptp_info->n_pins)
return -EINVAL;
if (on) {
period = rq->perout.period.sec * NSEC_PER_SEC +
rq->perout.period.nsec;
ptp_pps_on(ptp, on, period);
} else {
ptp_pps_on(ptp, on, period);
}
return 0;
default:
Expand Down Expand Up @@ -411,6 +425,7 @@ int otx2_ptp_init(struct otx2_nic *pfvf)
.name = "OcteonTX2 PTP",
.max_adj = 1000000000ull,
.n_ext_ts = 1,
.n_per_out = 1,
.n_pins = 1,
.pps = 0,
.pin_config = &ptp_ptr->extts_config,
Expand Down

0 comments on commit 35293cb

Please sign in to comment.