Skip to content

Commit

Permalink
octeontx2-af: Add external ptp input clock
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yi Guo authored and David S. Miller committed Sep 28, 2021
1 parent e266f66 commit 99bbc4a
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 6 deletions.
3 changes: 3 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/af/mbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
55 changes: 54 additions & 1 deletion drivers/net/ethernet/marvell/octeontx2/af/ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,22 @@

#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[];
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/marvell/octeontx2/af/ptp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion drivers/net/ethernet/marvell/octeontx2/af/rvu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/ethernet/marvell/octeontx2/af/rvu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
120 changes: 118 additions & 2 deletions drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 ?
Expand Down

0 comments on commit 99bbc4a

Please sign in to comment.