Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: ocelot: add hardware timestamping support for Felix

This patch is to reuse ocelot functions as possible to enable PTP
clock and to support hardware timestamping on Felix.
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: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Yangbo Lu and committed by
David S. Miller
c0bcf537 5df66c48

+102 -1
+89
drivers/net/dsa/ocelot/felix.c
··· 3 3 */ 4 4 #include <uapi/linux/if_bridge.h> 5 5 #include <soc/mscc/ocelot.h> 6 + #include <linux/packing.h> 6 7 #include <linux/module.h> 7 8 #include <linux/pci.h> 8 9 #include <linux/of.h> ··· 304 303 ocelot_deinit(ocelot); 305 304 } 306 305 306 + static int felix_hwtstamp_get(struct dsa_switch *ds, int port, 307 + struct ifreq *ifr) 308 + { 309 + struct ocelot *ocelot = ds->priv; 310 + 311 + return ocelot_hwstamp_get(ocelot, port, ifr); 312 + } 313 + 314 + static int felix_hwtstamp_set(struct dsa_switch *ds, int port, 315 + struct ifreq *ifr) 316 + { 317 + struct ocelot *ocelot = ds->priv; 318 + 319 + return ocelot_hwstamp_set(ocelot, port, ifr); 320 + } 321 + 322 + static bool felix_rxtstamp(struct dsa_switch *ds, int port, 323 + struct sk_buff *skb, unsigned int type) 324 + { 325 + struct skb_shared_hwtstamps *shhwtstamps; 326 + struct ocelot *ocelot = ds->priv; 327 + u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN; 328 + u32 tstamp_lo, tstamp_hi; 329 + struct timespec64 ts; 330 + u64 tstamp, val; 331 + 332 + ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); 333 + tstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 334 + 335 + packing(extraction, &val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0); 336 + tstamp_lo = (u32)val; 337 + 338 + tstamp_hi = tstamp >> 32; 339 + if ((tstamp & 0xffffffff) < tstamp_lo) 340 + tstamp_hi--; 341 + 342 + tstamp = ((u64)tstamp_hi << 32) | tstamp_lo; 343 + 344 + shhwtstamps = skb_hwtstamps(skb); 345 + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); 346 + shhwtstamps->hwtstamp = tstamp; 347 + return false; 348 + } 349 + 350 + bool felix_txtstamp(struct dsa_switch *ds, int port, 351 + struct sk_buff *clone, unsigned int type) 352 + { 353 + struct ocelot *ocelot = ds->priv; 354 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 355 + 356 + if (!ocelot_port_add_txtstamp_skb(ocelot_port, clone)) 357 + return true; 358 + 359 + return false; 360 + } 361 + 307 362 static const struct dsa_switch_ops felix_switch_ops = { 308 363 .get_tag_protocol = felix_get_tag_protocol, 309 364 .setup = felix_setup, ··· 382 325 .port_vlan_filtering = felix_vlan_filtering, 383 326 .port_vlan_add = felix_vlan_add, 384 327 .port_vlan_del = felix_vlan_del, 328 + .port_hwtstamp_get = felix_hwtstamp_get, 329 + .port_hwtstamp_set = felix_hwtstamp_set, 330 + .port_rxtstamp = felix_rxtstamp, 331 + .port_txtstamp = felix_txtstamp, 385 332 }; 386 333 387 334 static struct felix_info *felix_instance_tbl[] = { 388 335 [FELIX_INSTANCE_VSC9959] = &felix_info_vsc9959, 389 336 }; 337 + 338 + static irqreturn_t felix_irq_handler(int irq, void *data) 339 + { 340 + struct ocelot *ocelot = (struct ocelot *)data; 341 + 342 + /* The INTB interrupt is used for both PTP TX timestamp interrupt 343 + * and preemption status change interrupt on each port. 344 + * 345 + * - Get txtstamp if have 346 + * - TODO: handle preemption. Without handling it, driver may get 347 + * interrupt storm. 348 + */ 349 + 350 + ocelot_get_txtstamp(ocelot); 351 + 352 + return IRQ_HANDLED; 353 + } 390 354 391 355 static int felix_pci_probe(struct pci_dev *pdev, 392 356 const struct pci_device_id *id) ··· 450 372 451 373 pci_set_master(pdev); 452 374 375 + err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL, 376 + &felix_irq_handler, IRQF_ONESHOT, 377 + "felix-intb", ocelot); 378 + if (err) { 379 + dev_err(&pdev->dev, "Failed to request irq\n"); 380 + goto err_alloc_irq; 381 + } 382 + 383 + ocelot->ptp = 1; 384 + 453 385 ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL); 454 386 if (!ds) { 455 387 err = -ENOMEM; ··· 484 396 err_register_ds: 485 397 kfree(ds); 486 398 err_alloc_ds: 399 + err_alloc_irq: 487 400 err_alloc_felix: 488 401 kfree(felix); 489 402 err_dma:
+13 -1
net/dsa/tag_ocelot.c
··· 137 137 struct net_device *netdev) 138 138 { 139 139 struct dsa_port *dp = dsa_slave_to_port(netdev); 140 - u64 bypass, dest, src, qos_class; 140 + u64 bypass, dest, src, qos_class, rew_op; 141 141 struct dsa_switch *ds = dp->ds; 142 142 int port = dp->index; 143 + struct ocelot *ocelot = ds->priv; 144 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 143 145 u8 *injection; 144 146 145 147 if (unlikely(skb_cow_head(skb, OCELOT_TAG_LEN) < 0)) { ··· 162 160 packing(injection, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0); 163 161 packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0); 164 162 packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0); 163 + 164 + if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { 165 + rew_op = ocelot_port->ptp_cmd; 166 + if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { 167 + rew_op |= (ocelot_port->ts_id % 4) << 3; 168 + ocelot_port->ts_id++; 169 + } 170 + 171 + packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0); 172 + } 165 173 166 174 return skb; 167 175 }