]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
octeontx2-pf: Enable PTP PPS output support
authorHariprasad Kelam <hkelam@marvell.com>
Tue, 12 Sep 2023 17:51:16 +0000 (23:21 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Sep 2023 09:35:07 +0000 (10:35 +0100)
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>
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/ptp.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c

index 6b5b06c2b4e99600a28e1e8058d32f755605c644..c5bd7747fc0e1fbc44c699bbeeb5d4f677c97866 100644 (file)
@@ -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,
 };
@@ -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;
 };
index ffbd22797163f91323addd241f337d73ecdd08f6..bcc96eed24813e34611497044af967a75c041835 100644 (file)
@@ -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
@@ -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
@@ -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;
 }
 
@@ -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);
index 3a72b0793d4a734ad5787396fc7115d8d92416e5..63130ba37e9df1feb01b02a25d357e7bb7e95f7e 100644 (file)
@@ -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;
 
@@ -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);
 }
@@ -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;
        }
@@ -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)
@@ -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:
@@ -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,