Merge branch 'am65-cpts-PPS'
Siddharth Vadapalli says: ==================== Add PPS support to am65-cpts driver The CPTS hardware doesn't support PPS signal generation. Using the GenFx (periodic signal generator) function, it is possible to model a PPS signal followed by routing it via the time sync router to the CPTS_HWy_TS_PUSH (hardware time stamp) input, in order to generate timestamps at 1 second intervals. This series adds driver support for enabling PPS signal generation. Additionally, the documentation for the am65-cpts driver is updated with the bindings for the "ti,pps" property, which is used to inform the pair [CPTS_HWy_TS_PUSH, GenFx] to the cpts driver. Changes from v1: 1. Drop device-tree patches. 2. Address Roger's comments on the: "net: ethernet: ti: am65-cpts: add pps support" patch. 3. Collect Reviewed-by tag from Rob Herring. v1: https://lore.kernel.org/r/20230111114429.1297557-1-s-vadapalli@ti.com/ ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0852208fd5
|
@ -93,6 +93,14 @@ properties:
|
|||
description:
|
||||
Number of timestamp Generator function outputs (TS_GENFx)
|
||||
|
||||
ti,pps:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
description: |
|
||||
The pair of HWx_TS_PUSH input and TS_GENFy output indexes used for
|
||||
PPS events generation. Platform/board specific.
|
||||
|
||||
refclk-mux:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
|
|
@ -176,6 +176,10 @@ struct am65_cpts {
|
|||
u32 genf_enable;
|
||||
u32 hw_ts_enable;
|
||||
struct sk_buff_head txq;
|
||||
bool pps_enabled;
|
||||
bool pps_present;
|
||||
u32 pps_hw_ts_idx;
|
||||
u32 pps_genf_idx;
|
||||
/* context save/restore */
|
||||
u64 sr_cpts_ns;
|
||||
u64 sr_ktime_ns;
|
||||
|
@ -319,8 +323,15 @@ static int am65_cpts_fifo_read(struct am65_cpts *cpts)
|
|||
case AM65_CPTS_EV_HW:
|
||||
pevent.index = am65_cpts_event_get_port(event) - 1;
|
||||
pevent.timestamp = event->timestamp;
|
||||
pevent.type = PTP_CLOCK_EXTTS;
|
||||
dev_dbg(cpts->dev, "AM65_CPTS_EV_HW p:%d t:%llu\n",
|
||||
if (cpts->pps_enabled && pevent.index == cpts->pps_hw_ts_idx) {
|
||||
pevent.type = PTP_CLOCK_PPSUSR;
|
||||
pevent.pps_times.ts_real = ns_to_timespec64(pevent.timestamp);
|
||||
} else {
|
||||
pevent.type = PTP_CLOCK_EXTTS;
|
||||
}
|
||||
dev_dbg(cpts->dev, "AM65_CPTS_EV_HW:%s p:%d t:%llu\n",
|
||||
pevent.type == PTP_CLOCK_EXTTS ?
|
||||
"extts" : "pps",
|
||||
pevent.index, event->timestamp);
|
||||
|
||||
ptp_clock_event(cpts->ptp_clock, &pevent);
|
||||
|
@ -394,10 +405,13 @@ static irqreturn_t am65_cpts_interrupt(int irq, void *dev_id)
|
|||
static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||||
{
|
||||
struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
|
||||
u32 pps_ctrl_val = 0, pps_ppm_hi = 0, pps_ppm_low = 0;
|
||||
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
|
||||
int pps_index = cpts->pps_genf_idx;
|
||||
u64 adj_period, pps_adj_period;
|
||||
u32 ctrl_val, ppm_hi, ppm_low;
|
||||
unsigned long flags;
|
||||
int neg_adj = 0;
|
||||
u64 adj_period;
|
||||
u32 val;
|
||||
|
||||
if (ppb < 0) {
|
||||
neg_adj = 1;
|
||||
|
@ -417,17 +431,53 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
|
||||
mutex_lock(&cpts->ptp_clk_lock);
|
||||
|
||||
val = am65_cpts_read32(cpts, control);
|
||||
ctrl_val = am65_cpts_read32(cpts, control);
|
||||
if (neg_adj)
|
||||
val |= AM65_CPTS_CONTROL_TS_PPM_DIR;
|
||||
ctrl_val |= AM65_CPTS_CONTROL_TS_PPM_DIR;
|
||||
else
|
||||
val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;
|
||||
am65_cpts_write32(cpts, val, control);
|
||||
ctrl_val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;
|
||||
|
||||
val = upper_32_bits(adj_period) & 0x3FF;
|
||||
am65_cpts_write32(cpts, val, ts_ppm_hi);
|
||||
val = lower_32_bits(adj_period);
|
||||
am65_cpts_write32(cpts, val, ts_ppm_low);
|
||||
ppm_hi = upper_32_bits(adj_period) & 0x3FF;
|
||||
ppm_low = lower_32_bits(adj_period);
|
||||
|
||||
if (cpts->pps_enabled) {
|
||||
pps_ctrl_val = am65_cpts_read32(cpts, genf[pps_index].control);
|
||||
if (neg_adj)
|
||||
pps_ctrl_val &= ~BIT(1);
|
||||
else
|
||||
pps_ctrl_val |= BIT(1);
|
||||
|
||||
/* GenF PPM will do correction using cpts refclk tick which is
|
||||
* (cpts->ts_add_val + 1) ns, so GenF length PPM adj period
|
||||
* need to be corrected.
|
||||
*/
|
||||
pps_adj_period = adj_period * (cpts->ts_add_val + 1);
|
||||
pps_ppm_hi = upper_32_bits(pps_adj_period) & 0x3FF;
|
||||
pps_ppm_low = lower_32_bits(pps_adj_period);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
|
||||
/* All below writes must be done extremely fast:
|
||||
* - delay between PPM dir and PPM value changes can cause err due old
|
||||
* PPM correction applied in wrong direction
|
||||
* - delay between CPTS-clock PPM cfg and GenF PPM cfg can cause err
|
||||
* due CPTS-clock PPM working with new cfg while GenF PPM cfg still
|
||||
* with old for short period of time
|
||||
*/
|
||||
|
||||
am65_cpts_write32(cpts, ctrl_val, control);
|
||||
am65_cpts_write32(cpts, ppm_hi, ts_ppm_hi);
|
||||
am65_cpts_write32(cpts, ppm_low, ts_ppm_low);
|
||||
|
||||
if (cpts->pps_enabled) {
|
||||
am65_cpts_write32(cpts, pps_ctrl_val, genf[pps_index].control);
|
||||
am65_cpts_write32(cpts, pps_ppm_hi, genf[pps_index].ppm_hi);
|
||||
am65_cpts_write32(cpts, pps_ppm_low, genf[pps_index].ppm_low);
|
||||
}
|
||||
|
||||
/* All GenF/EstF can be updated here the same way */
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
mutex_unlock(&cpts->ptp_clk_lock);
|
||||
|
||||
|
@ -507,7 +557,13 @@ static void am65_cpts_extts_enable_hw(struct am65_cpts *cpts, u32 index, int on)
|
|||
|
||||
static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on)
|
||||
{
|
||||
if (!!(cpts->hw_ts_enable & BIT(index)) == !!on)
|
||||
if (index >= cpts->ptp_info.n_ext_ts)
|
||||
return -ENXIO;
|
||||
|
||||
if (cpts->pps_present && index == cpts->pps_hw_ts_idx)
|
||||
return -EINVAL;
|
||||
|
||||
if (((cpts->hw_ts_enable & BIT(index)) >> index) == on)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cpts->ptp_clk_lock);
|
||||
|
@ -591,6 +647,12 @@ static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,
|
|||
static int am65_cpts_perout_enable(struct am65_cpts *cpts,
|
||||
struct ptp_perout_request *req, int on)
|
||||
{
|
||||
if (req->index >= cpts->ptp_info.n_per_out)
|
||||
return -ENXIO;
|
||||
|
||||
if (cpts->pps_present && req->index == cpts->pps_genf_idx)
|
||||
return -EINVAL;
|
||||
|
||||
if (!!(cpts->genf_enable & BIT(req->index)) == !!on)
|
||||
return 0;
|
||||
|
||||
|
@ -604,6 +666,48 @@ static int am65_cpts_perout_enable(struct am65_cpts *cpts,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int am65_cpts_pps_enable(struct am65_cpts *cpts, int on)
|
||||
{
|
||||
int ret = 0;
|
||||
struct timespec64 ts;
|
||||
struct ptp_clock_request rq;
|
||||
u64 ns;
|
||||
|
||||
if (!cpts->pps_present)
|
||||
return -EINVAL;
|
||||
|
||||
if (cpts->pps_enabled == !!on)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cpts->ptp_clk_lock);
|
||||
|
||||
if (on) {
|
||||
am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);
|
||||
|
||||
ns = am65_cpts_gettime(cpts, NULL);
|
||||
ts = ns_to_timespec64(ns);
|
||||
rq.perout.period.sec = 1;
|
||||
rq.perout.period.nsec = 0;
|
||||
rq.perout.start.sec = ts.tv_sec + 2;
|
||||
rq.perout.start.nsec = 0;
|
||||
rq.perout.index = cpts->pps_genf_idx;
|
||||
|
||||
am65_cpts_perout_enable_hw(cpts, &rq.perout, on);
|
||||
cpts->pps_enabled = true;
|
||||
} else {
|
||||
rq.perout.index = cpts->pps_genf_idx;
|
||||
am65_cpts_perout_enable_hw(cpts, &rq.perout, on);
|
||||
am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);
|
||||
cpts->pps_enabled = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&cpts->ptp_clk_lock);
|
||||
|
||||
dev_dbg(cpts->dev, "%s: pps: %s\n",
|
||||
__func__, on ? "enabled" : "disabled");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on)
|
||||
{
|
||||
|
@ -614,6 +718,8 @@ static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,
|
|||
return am65_cpts_extts_enable(cpts, rq->extts.index, on);
|
||||
case PTP_CLK_REQ_PEROUT:
|
||||
return am65_cpts_perout_enable(cpts, &rq->perout, on);
|
||||
case PTP_CLK_REQ_PPS:
|
||||
return am65_cpts_pps_enable(cpts, on);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -926,6 +1032,23 @@ static int am65_cpts_of_parse(struct am65_cpts *cpts, struct device_node *node)
|
|||
if (!of_property_read_u32(node, "ti,cpts-periodic-outputs", &prop[0]))
|
||||
cpts->genf_num = prop[0];
|
||||
|
||||
if (!of_property_read_u32_array(node, "ti,pps", prop, 2)) {
|
||||
cpts->pps_present = true;
|
||||
|
||||
if (prop[0] > 7) {
|
||||
dev_err(cpts->dev, "invalid HWx_TS_PUSH index: %u provided\n", prop[0]);
|
||||
cpts->pps_present = false;
|
||||
}
|
||||
if (prop[1] > 1) {
|
||||
dev_err(cpts->dev, "invalid GENFy index: %u provided\n", prop[1]);
|
||||
cpts->pps_present = false;
|
||||
}
|
||||
if (cpts->pps_present) {
|
||||
cpts->pps_hw_ts_idx = prop[0];
|
||||
cpts->pps_genf_idx = prop[1];
|
||||
}
|
||||
}
|
||||
|
||||
return cpts_of_mux_clk_setup(cpts, node);
|
||||
}
|
||||
|
||||
|
@ -993,6 +1116,8 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
|
|||
cpts->ptp_info.n_ext_ts = cpts->ext_ts_inputs;
|
||||
if (cpts->genf_num)
|
||||
cpts->ptp_info.n_per_out = cpts->genf_num;
|
||||
if (cpts->pps_present)
|
||||
cpts->ptp_info.pps = 1;
|
||||
|
||||
am65_cpts_set_add_val(cpts);
|
||||
|
||||
|
@ -1028,9 +1153,9 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u\n",
|
||||
dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u pps:%d\n",
|
||||
am65_cpts_read32(cpts, idver),
|
||||
cpts->refclk_freq, cpts->ts_add_val);
|
||||
cpts->refclk_freq, cpts->ts_add_val, cpts->pps_present);
|
||||
|
||||
return cpts;
|
||||
|
||||
|
|
Loading…
Reference in New Issue