net: mscc: PTP Hardware Clock (PHC) support
This patch adds support for PTP Hardware Clock (PHC) to the Ocelot switch for both PTP 1-step and 2-step modes. Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com> Acked-by: Richard Cochran <richardcochran@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
1f0239de58
commit
4e3b0468e6
@@ -31,6 +31,8 @@ static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info)
|
||||
|
||||
info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80;
|
||||
|
||||
info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32);
|
||||
|
||||
info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4);
|
||||
|
||||
info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1);
|
||||
@@ -92,13 +94,14 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
|
||||
return IRQ_NONE;
|
||||
|
||||
do {
|
||||
struct sk_buff *skb;
|
||||
struct skb_shared_hwtstamps *shhwtstamps;
|
||||
u64 tod_in_ns, full_ts_in_ns;
|
||||
struct frame_info info = {};
|
||||
struct net_device *dev;
|
||||
u32 *buf;
|
||||
u32 ifh[4], val, *buf;
|
||||
struct timespec64 ts;
|
||||
int sz, len, buf_len;
|
||||
u32 ifh[4];
|
||||
u32 val;
|
||||
struct frame_info info;
|
||||
struct sk_buff *skb;
|
||||
|
||||
for (i = 0; i < IFH_LEN; i++) {
|
||||
err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]);
|
||||
@@ -145,6 +148,22 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ocelot->ptp) {
|
||||
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
|
||||
|
||||
tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
|
||||
if ((tod_in_ns & 0xffffffff) < info.timestamp)
|
||||
full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
|
||||
info.timestamp;
|
||||
else
|
||||
full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
|
||||
info.timestamp;
|
||||
|
||||
shhwtstamps = skb_hwtstamps(skb);
|
||||
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
|
||||
shhwtstamps->hwtstamp = full_ts_in_ns;
|
||||
}
|
||||
|
||||
/* Everything we see on an interface that is in the HW bridge
|
||||
* has already been forwarded.
|
||||
*/
|
||||
@@ -164,6 +183,66 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg)
|
||||
{
|
||||
int budget = OCELOT_PTP_QUEUE_SZ;
|
||||
struct ocelot *ocelot = arg;
|
||||
|
||||
while (budget--) {
|
||||
struct skb_shared_hwtstamps shhwtstamps;
|
||||
struct list_head *pos, *tmp;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct ocelot_skb *entry;
|
||||
struct ocelot_port *port;
|
||||
struct timespec64 ts;
|
||||
u32 val, id, txport;
|
||||
|
||||
val = ocelot_read(ocelot, SYS_PTP_STATUS);
|
||||
|
||||
/* Check if a timestamp can be retrieved */
|
||||
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
|
||||
break;
|
||||
|
||||
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
|
||||
|
||||
/* Retrieve the ts ID and Tx port */
|
||||
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
|
||||
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
|
||||
|
||||
/* Retrieve its associated skb */
|
||||
port = ocelot->ports[txport];
|
||||
|
||||
list_for_each_safe(pos, tmp, &port->skbs) {
|
||||
entry = list_entry(pos, struct ocelot_skb, head);
|
||||
if (entry->id != id)
|
||||
continue;
|
||||
|
||||
skb = entry->skb;
|
||||
|
||||
list_del(pos);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
/* Next ts */
|
||||
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
|
||||
|
||||
if (unlikely(!skb))
|
||||
continue;
|
||||
|
||||
/* Get the h/w timestamp */
|
||||
ocelot_get_hwtimestamp(ocelot, &ts);
|
||||
|
||||
/* Set the timestamp into the skb */
|
||||
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
|
||||
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
|
||||
skb_tstamp_tx(skb, &shhwtstamps);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct of_device_id mscc_ocelot_match[] = {
|
||||
{ .compatible = "mscc,vsc7514-switch" },
|
||||
{ }
|
||||
@@ -172,12 +251,12 @@ MODULE_DEVICE_TABLE(of, mscc_ocelot_match);
|
||||
|
||||
static int mscc_ocelot_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err, irq;
|
||||
unsigned int i;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *ports, *portnp;
|
||||
int err, irq_xtr, irq_ptp_rdy;
|
||||
struct ocelot *ocelot;
|
||||
struct regmap *hsio;
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
struct {
|
||||
@@ -232,16 +311,29 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "xtr");
|
||||
if (irq < 0)
|
||||
irq_xtr = platform_get_irq_byname(pdev, "xtr");
|
||||
if (irq_xtr < 0)
|
||||
return -ENODEV;
|
||||
|
||||
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL,
|
||||
ocelot_xtr_irq_handler, IRQF_ONESHOT,
|
||||
"frame extraction", ocelot);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy");
|
||||
if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) {
|
||||
err = devm_request_threaded_irq(&pdev->dev, irq_ptp_rdy, NULL,
|
||||
ocelot_ptp_rdy_irq_handler,
|
||||
IRQF_ONESHOT, "ptp ready",
|
||||
ocelot);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Both the PTP interrupt and the PTP bank are available */
|
||||
ocelot->ptp = 1;
|
||||
}
|
||||
|
||||
regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1);
|
||||
regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
|
||||
|
||||
|
Reference in New Issue
Block a user