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

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>

+222 -82
+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:
+16
drivers/net/dsa/ocelot/felix_vsc9959.c
··· 282 282 REG_RESERVED(SYS_CM_DATA), 283 283 }; 284 284 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 + 285 295 static const u32 vsc9959_gcb_regmap[] = { 286 296 REG(GCB_SOFT_RST, 0x000004), 287 297 }; ··· 303 293 [REW] = vsc9959_rew_regmap, 304 294 [SYS] = vsc9959_sys_regmap, 305 295 [S2] = vsc9959_s2_regmap, 296 + [PTP] = vsc9959_ptp_regmap, 306 297 [GCB] = vsc9959_gcb_regmap, 307 298 }; 308 299 ··· 340 329 .start = 0x0060000, 341 330 .end = 0x00603ff, 342 331 .name = "s2", 332 + }, 333 + [PTP] = { 334 + .start = 0x0090000, 335 + .end = 0x00900cb, 336 + .name = "ptp", 343 337 }, 344 338 [GCB] = { 345 339 .start = 0x0070000,
+91 -22
drivers/net/ethernet/mscc/ocelot.c
··· 575 575 return 0; 576 576 } 577 577 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 + 578 604 static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) 579 605 { 580 606 struct ocelot_port_private *priv = netdev_priv(dev); ··· 663 637 dev->stats.tx_packets++; 664 638 dev->stats.tx_bytes += skb->len; 665 639 666 - if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP && 667 - ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { 668 - struct ocelot_skb *oskb = 669 - kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC); 670 - 671 - if (unlikely(!oskb)) 672 - goto out; 673 - 674 - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 675 - 676 - oskb->skb = skb; 677 - oskb->id = ocelot_port->ts_id % 4; 640 + if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) { 678 641 ocelot_port->ts_id++; 679 - 680 - list_add_tail(&oskb->head, &ocelot_port->skbs); 681 - 682 642 return NETDEV_TX_OK; 683 643 } 684 644 685 - out: 686 645 dev_kfree_skb_any(skb); 687 646 return NETDEV_TX_OK; 688 647 } 689 648 690 - void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts) 649 + static void ocelot_get_hwtimestamp(struct ocelot *ocelot, 650 + struct timespec64 *ts) 691 651 { 692 652 unsigned long flags; 693 653 u32 val; ··· 698 686 699 687 spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); 700 688 } 701 - EXPORT_SYMBOL(ocelot_get_hwtimestamp); 689 + 690 + void ocelot_get_txtstamp(struct ocelot *ocelot) 691 + { 692 + int budget = OCELOT_PTP_QUEUE_SZ; 693 + 694 + while (budget--) { 695 + struct skb_shared_hwtstamps shhwtstamps; 696 + struct list_head *pos, *tmp; 697 + struct sk_buff *skb = NULL; 698 + struct ocelot_skb *entry; 699 + struct ocelot_port *port; 700 + struct timespec64 ts; 701 + u32 val, id, txport; 702 + 703 + val = ocelot_read(ocelot, SYS_PTP_STATUS); 704 + 705 + /* Check if a timestamp can be retrieved */ 706 + if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD)) 707 + break; 708 + 709 + WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL); 710 + 711 + /* Retrieve the ts ID and Tx port */ 712 + id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); 713 + txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); 714 + 715 + /* Retrieve its associated skb */ 716 + port = ocelot->ports[txport]; 717 + 718 + list_for_each_safe(pos, tmp, &port->skbs) { 719 + entry = list_entry(pos, struct ocelot_skb, head); 720 + if (entry->id != id) 721 + continue; 722 + 723 + skb = entry->skb; 724 + 725 + list_del(pos); 726 + kfree(entry); 727 + } 728 + 729 + /* Next ts */ 730 + ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); 731 + 732 + if (unlikely(!skb)) 733 + continue; 734 + 735 + /* Get the h/w timestamp */ 736 + ocelot_get_hwtimestamp(ocelot, &ts); 737 + 738 + /* Set the timestamp into the skb */ 739 + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 740 + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 741 + skb_tstamp_tx(skb, &shhwtstamps); 742 + 743 + dev_kfree_skb_any(skb); 744 + } 745 + } 746 + EXPORT_SYMBOL(ocelot_get_txtstamp); 702 747 703 748 static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) 704 749 { ··· 1118 1049 return 0; 1119 1050 } 1120 1051 1121 - static int ocelot_hwstamp_get(struct ocelot *ocelot, int port, 1122 - struct ifreq *ifr) 1052 + int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) 1123 1053 { 1124 1054 return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, 1125 1055 sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; 1126 1056 } 1057 + EXPORT_SYMBOL(ocelot_hwstamp_get); 1127 1058 1128 - static int ocelot_hwstamp_set(struct ocelot *ocelot, int port, 1129 - struct ifreq *ifr) 1059 + int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) 1130 1060 { 1131 1061 struct ocelot_port *ocelot_port = ocelot->ports[port]; 1132 1062 struct hwtstamp_config cfg; ··· 1188 1120 1189 1121 return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 1190 1122 } 1123 + EXPORT_SYMBOL(ocelot_hwstamp_set); 1191 1124 1192 1125 static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 1193 1126 {
-6
drivers/net/ethernet/mscc/ocelot.h
··· 74 74 struct ocelot_port_tc tc; 75 75 }; 76 76 77 - struct ocelot_skb { 78 - struct list_head head; 79 - struct sk_buff *skb; 80 - u8 id; 81 - }; 82 - 83 77 u32 ocelot_port_readl(struct ocelot_port *port, u32 reg); 84 78 void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg); 85 79
+1 -52
drivers/net/ethernet/mscc/ocelot_board.c
··· 190 190 191 191 static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) 192 192 { 193 - int budget = OCELOT_PTP_QUEUE_SZ; 194 193 struct ocelot *ocelot = arg; 195 194 196 - while (budget--) { 197 - struct skb_shared_hwtstamps shhwtstamps; 198 - struct list_head *pos, *tmp; 199 - struct sk_buff *skb = NULL; 200 - struct ocelot_skb *entry; 201 - struct ocelot_port *port; 202 - struct timespec64 ts; 203 - u32 val, id, txport; 204 - 205 - val = ocelot_read(ocelot, SYS_PTP_STATUS); 206 - 207 - /* Check if a timestamp can be retrieved */ 208 - if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD)) 209 - break; 210 - 211 - WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL); 212 - 213 - /* Retrieve the ts ID and Tx port */ 214 - id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); 215 - txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); 216 - 217 - /* Retrieve its associated skb */ 218 - port = ocelot->ports[txport]; 219 - 220 - list_for_each_safe(pos, tmp, &port->skbs) { 221 - entry = list_entry(pos, struct ocelot_skb, head); 222 - if (entry->id != id) 223 - continue; 224 - 225 - skb = entry->skb; 226 - 227 - list_del(pos); 228 - kfree(entry); 229 - } 230 - 231 - /* Next ts */ 232 - ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); 233 - 234 - if (unlikely(!skb)) 235 - continue; 236 - 237 - /* Get the h/w timestamp */ 238 - ocelot_get_hwtimestamp(ocelot, &ts); 239 - 240 - /* Set the timestamp into the skb */ 241 - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 242 - shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 243 - skb_tstamp_tx(skb, &shhwtstamps); 244 - 245 - dev_kfree_skb_any(skb); 246 - } 195 + ocelot_get_txtstamp(ocelot); 247 196 248 197 return IRQ_HANDLED; 249 198 }
+12 -1
include/soc/mscc/ocelot.h
··· 406 406 int (*reset)(struct ocelot *ocelot); 407 407 }; 408 408 409 + struct ocelot_skb { 410 + struct list_head head; 411 + struct sk_buff *skb; 412 + u8 id; 413 + }; 414 + 415 + 409 416 struct ocelot_port { 410 417 struct ocelot *ocelot; 411 418 ··· 540 533 int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, 541 534 bool untagged); 542 535 int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid); 536 + int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr); 537 + int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr); 543 538 int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); 544 - void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts); 539 + int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, 540 + struct sk_buff *skb); 541 + void ocelot_get_txtstamp(struct ocelot *ocelot); 545 542 546 543 #endif
+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 }