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:
David S. Miller 2023-01-18 12:56:38 +00:00
commit 0852208fd5
2 changed files with 148 additions and 15 deletions

View File

@ -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

View File

@ -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;