octeontx2-af: Add external ptp input clock
authorYi Guo <yig@marvell.com>
Tue, 28 Sep 2021 11:31:01 +0000 (17:01 +0530)
committerDavid S. Miller <davem@davemloft.net>
Tue, 28 Sep 2021 12:50:37 +0000 (13:50 +0100)
PTP hardware block can be configured to utilize
the external clock. Also the current ptp timestamp
can be captured when external trigger is applied on
a gpio pin. These features are required in scenarios
like connecting a external timing device to the chip
for time synchronization. The timing device provides
the clock and trigger(PPS signal) to the PTP block.
This patch does the following:
1. configures PTP block to use external clock
frequency and timestamp capture on external event.
2. sends PTP_REQ_EXTTS events to kernel ptp phc susbsytem
with captured timestamps
3. aligns PPS edge to adjusted ptp clock in the ptp device
by setting the PPS_THRESH to the reminder of the last
timestamp value captured by external PPS

Signed-off-by: Yi Guo <yig@marvell.com>
Signed-off-by: Hariprasad Kelam <hkelam@marvell.com>
Signed-off-by: Subbaraya Sundeep <sbhatta@marvell.com>
Signed-off-by: Sunil Goutham <sgoutham@marvell.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/af/ptp.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.c
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c

index b5ee324..dfe4872 100644 (file)
@@ -1420,12 +1420,15 @@ struct npc_mcam_get_stats_rsp {
 enum ptp_op {
        PTP_OP_ADJFINE = 0,
        PTP_OP_GET_CLOCK = 1,
+       PTP_OP_GET_TSTMP = 2,
+       PTP_OP_SET_THRESH = 3,
 };
 
 struct ptp_req {
        struct mbox_msghdr hdr;
        u8 op;
        s64 scaled_ppm;
+       u64 thresh;
 };
 
 struct ptp_rsp {
index 477491c..d6321de 100644 (file)
 
 #define PTP_CLOCK_CFG                          0xF00ULL
 #define PTP_CLOCK_CFG_PTP_EN                   BIT_ULL(0)
+#define PTP_CLOCK_CFG_EXT_CLK_EN               BIT_ULL(1)
+#define PTP_CLOCK_CFG_EXT_CLK_IN_MASK          GENMASK_ULL(7, 2)
+#define PTP_CLOCK_CFG_TSTMP_EDGE               BIT_ULL(9)
+#define PTP_CLOCK_CFG_TSTMP_EN                 BIT_ULL(8)
+#define PTP_CLOCK_CFG_TSTMP_IN_MASK            GENMASK_ULL(15, 10)
+#define PTP_CLOCK_CFG_PPS_EN                   BIT_ULL(30)
+#define PTP_CLOCK_CFG_PPS_INV                  BIT_ULL(31)
+
+#define PTP_PPS_HI_INCR                                0xF60ULL
+#define PTP_PPS_LO_INCR                                0xF68ULL
+#define PTP_PPS_THRESH_HI                      0xF58ULL
+
 #define PTP_CLOCK_LO                           0xF08ULL
 #define PTP_CLOCK_HI                           0xF10ULL
 #define PTP_CLOCK_COMP                         0xF18ULL
+#define PTP_TIMESTAMP                          0xF20ULL
 
 static struct ptp *first_ptp_block;
 static const struct pci_device_id ptp_id_table[];
@@ -107,7 +120,7 @@ static int ptp_get_clock(struct ptp *ptp, u64 *clk)
        return 0;
 }
 
-void ptp_start(struct ptp *ptp, u64 sclk)
+void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts)
 {
        struct pci_dev *pdev;
        u64 clock_comp;
@@ -128,14 +141,48 @@ void ptp_start(struct ptp *ptp, u64 sclk)
 
        /* Enable PTP clock */
        clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
+
+       if (ext_clk_freq) {
+               ptp->clock_rate = ext_clk_freq;
+               /* Set GPIO as PTP clock source */
+               clock_cfg &= ~PTP_CLOCK_CFG_EXT_CLK_IN_MASK;
+               clock_cfg |= PTP_CLOCK_CFG_EXT_CLK_EN;
+       }
+
+       if (extts) {
+               clock_cfg |= PTP_CLOCK_CFG_TSTMP_EDGE;
+               /* Set GPIO as timestamping source */
+               clock_cfg &= ~PTP_CLOCK_CFG_TSTMP_IN_MASK;
+               clock_cfg |= PTP_CLOCK_CFG_TSTMP_EN;
+       }
+
        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);
 
+       /* 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);
+
        clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate;
        /* Initial compensation value to start the nanosecs counter */
        writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP);
 }
 
+static int ptp_get_tstmp(struct ptp *ptp, u64 *clk)
+{
+       *clk = readq(ptp->reg_base + PTP_TIMESTAMP);
+
+       return 0;
+}
+
+static int ptp_set_thresh(struct ptp *ptp, u64 thresh)
+{
+       writeq(thresh, ptp->reg_base + PTP_PPS_THRESH_HI);
+
+       return 0;
+}
+
 static int ptp_probe(struct pci_dev *pdev,
                     const struct pci_device_id *ent)
 {
@@ -250,6 +297,12 @@ int rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req,
        case PTP_OP_GET_CLOCK:
                err = ptp_get_clock(rvu->ptp, &rsp->clk);
                break;
+       case PTP_OP_GET_TSTMP:
+               err = ptp_get_tstmp(rvu->ptp, &rsp->clk);
+               break;
+       case PTP_OP_SET_THRESH:
+               err = ptp_set_thresh(rvu->ptp, req->thresh);
+               break;
        default:
                err = -EINVAL;
                break;
index 1ed350a..1b81a04 100644 (file)
@@ -20,7 +20,7 @@ struct ptp {
 
 struct ptp *ptp_get(void);
 void ptp_put(struct ptp *ptp);
-void ptp_start(struct ptp *ptp, u64 sclk);
+void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts);
 
 extern struct pci_driver ptp_driver;
 
index 87a32a1..4cb24e9 100644 (file)
@@ -3241,7 +3241,8 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        mutex_init(&rvu->rswitch.switch_lock);
 
        if (rvu->fwdata)
-               ptp_start(rvu->ptp, rvu->fwdata->sclk);
+               ptp_start(rvu->ptp, rvu->fwdata->sclk, rvu->fwdata->ptp_ext_clk_rate,
+                         rvu->fwdata->ptp_ext_tstamp);
 
        return 0;
 err_dl:
index 95e8076..58b1666 100644 (file)
@@ -396,7 +396,9 @@ struct rvu_fwdata {
        u64 mcam_addr;
        u64 mcam_sz;
        u64 msixtr_base;
-#define FWDATA_RESERVED_MEM 1023
+       u32 ptp_ext_clk_rate;
+       u32 ptp_ext_tstamp;
+#define FWDATA_RESERVED_MEM 1022
        u64 reserved[FWDATA_RESERVED_MEM];
 #define CGX_MAX         5
 #define CGX_LMACS_MAX   4
index 8e51a1d..0a792fc 100644 (file)
@@ -264,6 +264,12 @@ struct otx2_ptp {
 
        struct cyclecounter cycle_counter;
        struct timecounter time_counter;
+
+       struct delayed_work extts_work;
+       u64 last_extts;
+       u64 thresh;
+
+       struct ptp_pin_desc extts_config;
 };
 
 #define OTX2_HW_TIMESTAMP_LEN  8
index ec9e499..5e3056a 100644 (file)
@@ -27,6 +27,23 @@ static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
        return otx2_sync_mbox_msg(&ptp->nic->mbox);
 }
 
+static int ptp_set_thresh(struct otx2_ptp *ptp, u64 thresh)
+{
+       struct ptp_req *req;
+
+       if (!ptp->nic)
+               return -ENODEV;
+
+       req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
+       if (!req)
+               return -ENOMEM;
+
+       req->op = PTP_OP_SET_THRESH;
+       req->thresh = thresh;
+
+       return otx2_sync_mbox_msg(&ptp->nic->mbox);
+}
+
 static u64 ptp_cc_read(const struct cyclecounter *cc)
 {
        struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter);
@@ -55,6 +72,33 @@ static u64 ptp_cc_read(const struct cyclecounter *cc)
        return rsp->clk;
 }
 
+static u64 ptp_tstmp_read(struct otx2_ptp *ptp)
+{
+       struct ptp_req *req;
+       struct ptp_rsp *rsp;
+       int err;
+
+       if (!ptp->nic)
+               return 0;
+
+       req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
+       if (!req)
+               return 0;
+
+       req->op = PTP_OP_GET_TSTMP;
+
+       err = otx2_sync_mbox_msg(&ptp->nic->mbox);
+       if (err)
+               return 0;
+
+       rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0,
+                                                 &req->hdr);
+       if (IS_ERR(rsp))
+               return 0;
+
+       return rsp->clk;
+}
+
 static int otx2_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
 {
        struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
@@ -102,9 +146,73 @@ static int otx2_ptp_settime(struct ptp_clock_info *ptp_info,
        return 0;
 }
 
+static int otx2_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
+                              enum ptp_pin_function func, unsigned int chan)
+{
+       switch (func) {
+       case PTP_PF_NONE:
+       case PTP_PF_EXTTS:
+               break;
+       case PTP_PF_PEROUT:
+       case PTP_PF_PHYSYNC:
+               return -1;
+       }
+       return 0;
+}
+
+static void otx2_ptp_extts_check(struct work_struct *work)
+{
+       struct otx2_ptp *ptp = container_of(work, struct otx2_ptp,
+                                           extts_work.work);
+       struct ptp_clock_event event;
+       u64 tstmp, new_thresh;
+
+       mutex_lock(&ptp->nic->mbox.lock);
+       tstmp = ptp_tstmp_read(ptp);
+       mutex_unlock(&ptp->nic->mbox.lock);
+
+       if (tstmp != ptp->last_extts) {
+               event.type = PTP_CLOCK_EXTTS;
+               event.index = 0;
+               event.timestamp = timecounter_cyc2time(&ptp->time_counter, tstmp);
+               ptp_clock_event(ptp->ptp_clock, &event);
+               ptp->last_extts = tstmp;
+
+               new_thresh = tstmp % 500000000;
+               if (ptp->thresh != new_thresh) {
+                       mutex_lock(&ptp->nic->mbox.lock);
+                       ptp_set_thresh(ptp, new_thresh);
+                       mutex_unlock(&ptp->nic->mbox.lock);
+                       ptp->thresh = new_thresh;
+               }
+       }
+       schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200));
+}
+
 static int otx2_ptp_enable(struct ptp_clock_info *ptp_info,
                           struct ptp_clock_request *rq, int on)
 {
+       struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
+                                           ptp_info);
+       int pin = -1;
+
+       if (!ptp->nic)
+               return -ENODEV;
+
+       switch (rq->type) {
+       case PTP_CLK_REQ_EXTTS:
+               pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_EXTTS,
+                                  rq->extts.index);
+               if (pin < 0)
+                       return -EBUSY;
+               if (on)
+                       schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200));
+               else
+                       cancel_delayed_work_sync(&ptp->extts_work);
+               return 0;
+       default:
+               break;
+       }
        return -EOPNOTSUPP;
 }
 
@@ -149,20 +257,28 @@ int otx2_ptp_init(struct otx2_nic *pfvf)
        timecounter_init(&ptp_ptr->time_counter, &ptp_ptr->cycle_counter,
                         ktime_to_ns(ktime_get_real()));
 
+       snprintf(ptp_ptr->extts_config.name, sizeof(ptp_ptr->extts_config.name), "TSTAMP");
+       ptp_ptr->extts_config.index = 0;
+       ptp_ptr->extts_config.func = PTP_PF_NONE;
+
        ptp_ptr->ptp_info = (struct ptp_clock_info) {
                .owner          = THIS_MODULE,
                .name           = "OcteonTX2 PTP",
                .max_adj        = 1000000000ull,
-               .n_ext_ts       = 0,
-               .n_pins         = 0,
+               .n_ext_ts       = 1,
+               .n_pins         = 1,
                .pps            = 0,
+               .pin_config     = &ptp_ptr->extts_config,
                .adjfine        = otx2_ptp_adjfine,
                .adjtime        = otx2_ptp_adjtime,
                .gettime64      = otx2_ptp_gettime,
                .settime64      = otx2_ptp_settime,
                .enable         = otx2_ptp_enable,
+               .verify         = otx2_ptp_verify_pin,
        };
 
+       INIT_DELAYED_WORK(&ptp_ptr->extts_work, otx2_ptp_extts_check);
+
        ptp_ptr->ptp_clock = ptp_clock_register(&ptp_ptr->ptp_info, pfvf->dev);
        if (IS_ERR_OR_NULL(ptp_ptr->ptp_clock)) {
                err = ptp_ptr->ptp_clock ?