octeontx2-af: Add external ptp input clock
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>
This commit is contained in:
parent
e266f66393
commit
99bbc4ae69
@ -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 {
|
||||
|
@ -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[];
|
||||
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ?
|
||||
|
Loading…
x
Reference in New Issue
Block a user