Skip to content

Commit 2c44713

Browse files
committed
Merge branch 'DSA-Felix-PTP'
Yangbo Lu says: ==================== Support PTP clock and hardware timestamping for DSA Felix driver This patch-set is to support PTP clock and hardware timestamping for DSA Felix driver. Some functions in ocelot.c/ocelot_board.c driver were reworked/exported, so that DSA Felix driver was able to reuse them as much as possible. On TX path, timestamping works on packet which requires timestamp. The injection header will be configured accordingly, and skb clone requires timestamp will be added into a list. The TX timestamp is final handled in threaded interrupt handler when PTP timestamp FIFO is ready. On RX path, timestamping is always working. The RX timestamp could be got from extraction header. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
2 parents 8163999 + c0bcf53 commit 2c44713

File tree

7 files changed

+222
-82
lines changed

7 files changed

+222
-82
lines changed

drivers/net/dsa/ocelot/felix.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
#include <uapi/linux/if_bridge.h>
55
#include <soc/mscc/ocelot.h>
6+
#include <linux/packing.h>
67
#include <linux/module.h>
78
#include <linux/pci.h>
89
#include <linux/of.h>
@@ -303,6 +304,62 @@ static void felix_teardown(struct dsa_switch *ds)
303304
ocelot_deinit(ocelot);
304305
}
305306

307+
static int felix_hwtstamp_get(struct dsa_switch *ds, int port,
308+
struct ifreq *ifr)
309+
{
310+
struct ocelot *ocelot = ds->priv;
311+
312+
return ocelot_hwstamp_get(ocelot, port, ifr);
313+
}
314+
315+
static int felix_hwtstamp_set(struct dsa_switch *ds, int port,
316+
struct ifreq *ifr)
317+
{
318+
struct ocelot *ocelot = ds->priv;
319+
320+
return ocelot_hwstamp_set(ocelot, port, ifr);
321+
}
322+
323+
static bool felix_rxtstamp(struct dsa_switch *ds, int port,
324+
struct sk_buff *skb, unsigned int type)
325+
{
326+
struct skb_shared_hwtstamps *shhwtstamps;
327+
struct ocelot *ocelot = ds->priv;
328+
u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN;
329+
u32 tstamp_lo, tstamp_hi;
330+
struct timespec64 ts;
331+
u64 tstamp, val;
332+
333+
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
334+
tstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
335+
336+
packing(extraction, &val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0);
337+
tstamp_lo = (u32)val;
338+
339+
tstamp_hi = tstamp >> 32;
340+
if ((tstamp & 0xffffffff) < tstamp_lo)
341+
tstamp_hi--;
342+
343+
tstamp = ((u64)tstamp_hi << 32) | tstamp_lo;
344+
345+
shhwtstamps = skb_hwtstamps(skb);
346+
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
347+
shhwtstamps->hwtstamp = tstamp;
348+
return false;
349+
}
350+
351+
bool felix_txtstamp(struct dsa_switch *ds, int port,
352+
struct sk_buff *clone, unsigned int type)
353+
{
354+
struct ocelot *ocelot = ds->priv;
355+
struct ocelot_port *ocelot_port = ocelot->ports[port];
356+
357+
if (!ocelot_port_add_txtstamp_skb(ocelot_port, clone))
358+
return true;
359+
360+
return false;
361+
}
362+
306363
static const struct dsa_switch_ops felix_switch_ops = {
307364
.get_tag_protocol = felix_get_tag_protocol,
308365
.setup = felix_setup,
@@ -325,12 +382,33 @@ static const struct dsa_switch_ops felix_switch_ops = {
325382
.port_vlan_filtering = felix_vlan_filtering,
326383
.port_vlan_add = felix_vlan_add,
327384
.port_vlan_del = felix_vlan_del,
385+
.port_hwtstamp_get = felix_hwtstamp_get,
386+
.port_hwtstamp_set = felix_hwtstamp_set,
387+
.port_rxtstamp = felix_rxtstamp,
388+
.port_txtstamp = felix_txtstamp,
328389
};
329390

330391
static struct felix_info *felix_instance_tbl[] = {
331392
[FELIX_INSTANCE_VSC9959] = &felix_info_vsc9959,
332393
};
333394

395+
static irqreturn_t felix_irq_handler(int irq, void *data)
396+
{
397+
struct ocelot *ocelot = (struct ocelot *)data;
398+
399+
/* The INTB interrupt is used for both PTP TX timestamp interrupt
400+
* and preemption status change interrupt on each port.
401+
*
402+
* - Get txtstamp if have
403+
* - TODO: handle preemption. Without handling it, driver may get
404+
* interrupt storm.
405+
*/
406+
407+
ocelot_get_txtstamp(ocelot);
408+
409+
return IRQ_HANDLED;
410+
}
411+
334412
static int felix_pci_probe(struct pci_dev *pdev,
335413
const struct pci_device_id *id)
336414
{
@@ -372,6 +450,16 @@ static int felix_pci_probe(struct pci_dev *pdev,
372450

373451
pci_set_master(pdev);
374452

453+
err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL,
454+
&felix_irq_handler, IRQF_ONESHOT,
455+
"felix-intb", ocelot);
456+
if (err) {
457+
dev_err(&pdev->dev, "Failed to request irq\n");
458+
goto err_alloc_irq;
459+
}
460+
461+
ocelot->ptp = 1;
462+
375463
ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL);
376464
if (!ds) {
377465
err = -ENOMEM;
@@ -396,6 +484,7 @@ static int felix_pci_probe(struct pci_dev *pdev,
396484
err_register_ds:
397485
kfree(ds);
398486
err_alloc_ds:
487+
err_alloc_irq:
399488
err_alloc_felix:
400489
kfree(felix);
401490
err_dma:

drivers/net/dsa/ocelot/felix_vsc9959.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ static const u32 vsc9959_sys_regmap[] = {
282282
REG_RESERVED(SYS_CM_DATA),
283283
};
284284

285+
static const u32 vsc9959_ptp_regmap[] = {
286+
REG(PTP_PIN_CFG, 0x000000),
287+
REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
288+
REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
289+
REG(PTP_PIN_TOD_NSEC, 0x00000c),
290+
REG(PTP_CFG_MISC, 0x0000a0),
291+
REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
292+
REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
293+
};
294+
285295
static const u32 vsc9959_gcb_regmap[] = {
286296
REG(GCB_SOFT_RST, 0x000004),
287297
};
@@ -293,6 +303,7 @@ static const u32 *vsc9959_regmap[] = {
293303
[REW] = vsc9959_rew_regmap,
294304
[SYS] = vsc9959_sys_regmap,
295305
[S2] = vsc9959_s2_regmap,
306+
[PTP] = vsc9959_ptp_regmap,
296307
[GCB] = vsc9959_gcb_regmap,
297308
};
298309

@@ -330,6 +341,11 @@ static struct resource vsc9959_target_io_res[] = {
330341
.end = 0x00603ff,
331342
.name = "s2",
332343
},
344+
[PTP] = {
345+
.start = 0x0090000,
346+
.end = 0x00900cb,
347+
.name = "ptp",
348+
},
333349
[GCB] = {
334350
.start = 0x0070000,
335351
.end = 0x00701ff,

drivers/net/ethernet/mscc/ocelot.c

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,32 @@ static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
575575
return 0;
576576
}
577577

578+
int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
579+
struct sk_buff *skb)
580+
{
581+
struct skb_shared_info *shinfo = skb_shinfo(skb);
582+
struct ocelot *ocelot = ocelot_port->ocelot;
583+
584+
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
585+
ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
586+
struct ocelot_skb *oskb =
587+
kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC);
588+
589+
if (unlikely(!oskb))
590+
return -ENOMEM;
591+
592+
shinfo->tx_flags |= SKBTX_IN_PROGRESS;
593+
594+
oskb->skb = skb;
595+
oskb->id = ocelot_port->ts_id % 4;
596+
597+
list_add_tail(&oskb->head, &ocelot_port->skbs);
598+
return 0;
599+
}
600+
return -ENODATA;
601+
}
602+
EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
603+
578604
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
579605
{
580606
struct ocelot_port_private *priv = netdev_priv(dev);
@@ -637,31 +663,17 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
637663
dev->stats.tx_packets++;
638664
dev->stats.tx_bytes += skb->len;
639665

640-
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
641-
ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
642-
struct ocelot_skb *oskb =
643-
kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC);
644-
645-
if (unlikely(!oskb))
646-
goto out;
647-
648-
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
649-
650-
oskb->skb = skb;
651-
oskb->id = ocelot_port->ts_id % 4;
666+
if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
652667
ocelot_port->ts_id++;
653-
654-
list_add_tail(&oskb->head, &ocelot_port->skbs);
655-
656668
return NETDEV_TX_OK;
657669
}
658670

659-
out:
660671
dev_kfree_skb_any(skb);
661672
return NETDEV_TX_OK;
662673
}
663674

664-
void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts)
675+
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
676+
struct timespec64 *ts)
665677
{
666678
unsigned long flags;
667679
u32 val;
@@ -686,7 +698,64 @@ void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts)
686698

687699
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
688700
}
689-
EXPORT_SYMBOL(ocelot_get_hwtimestamp);
701+
702+
void ocelot_get_txtstamp(struct ocelot *ocelot)
703+
{
704+
int budget = OCELOT_PTP_QUEUE_SZ;
705+
706+
while (budget--) {
707+
struct skb_shared_hwtstamps shhwtstamps;
708+
struct list_head *pos, *tmp;
709+
struct sk_buff *skb = NULL;
710+
struct ocelot_skb *entry;
711+
struct ocelot_port *port;
712+
struct timespec64 ts;
713+
u32 val, id, txport;
714+
715+
val = ocelot_read(ocelot, SYS_PTP_STATUS);
716+
717+
/* Check if a timestamp can be retrieved */
718+
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
719+
break;
720+
721+
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
722+
723+
/* Retrieve the ts ID and Tx port */
724+
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
725+
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
726+
727+
/* Retrieve its associated skb */
728+
port = ocelot->ports[txport];
729+
730+
list_for_each_safe(pos, tmp, &port->skbs) {
731+
entry = list_entry(pos, struct ocelot_skb, head);
732+
if (entry->id != id)
733+
continue;
734+
735+
skb = entry->skb;
736+
737+
list_del(pos);
738+
kfree(entry);
739+
}
740+
741+
/* Next ts */
742+
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
743+
744+
if (unlikely(!skb))
745+
continue;
746+
747+
/* Get the h/w timestamp */
748+
ocelot_get_hwtimestamp(ocelot, &ts);
749+
750+
/* Set the timestamp into the skb */
751+
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
752+
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
753+
skb_tstamp_tx(skb, &shhwtstamps);
754+
755+
dev_kfree_skb_any(skb);
756+
}
757+
}
758+
EXPORT_SYMBOL(ocelot_get_txtstamp);
690759

691760
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
692761
{
@@ -1049,15 +1118,14 @@ static int ocelot_get_port_parent_id(struct net_device *dev,
10491118
return 0;
10501119
}
10511120

1052-
static int ocelot_hwstamp_get(struct ocelot *ocelot, int port,
1053-
struct ifreq *ifr)
1121+
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
10541122
{
10551123
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
10561124
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
10571125
}
1126+
EXPORT_SYMBOL(ocelot_hwstamp_get);
10581127

1059-
static int ocelot_hwstamp_set(struct ocelot *ocelot, int port,
1060-
struct ifreq *ifr)
1128+
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
10611129
{
10621130
struct ocelot_port *ocelot_port = ocelot->ports[port];
10631131
struct hwtstamp_config cfg;
@@ -1120,6 +1188,7 @@ static int ocelot_hwstamp_set(struct ocelot *ocelot, int port,
11201188

11211189
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
11221190
}
1191+
EXPORT_SYMBOL(ocelot_hwstamp_set);
11231192

11241193
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
11251194
{

drivers/net/ethernet/mscc/ocelot.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,6 @@ struct ocelot_port_private {
7474
struct ocelot_port_tc tc;
7575
};
7676

77-
struct ocelot_skb {
78-
struct list_head head;
79-
struct sk_buff *skb;
80-
u8 id;
81-
};
82-
8377
u32 ocelot_port_readl(struct ocelot_port *port, u32 reg);
8478
void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
8579

0 commit comments

Comments
 (0)