static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) { struct bfin_mac_local *lp = netdev_priv(netdev); u32 valid; u64 regval, ns; struct skb_shared_hwtstamps *shhwtstamps; if (bfin_mac_hwtstamp_is_none(lp->stamp_cfg.rx_filter)) return; valid = bfin_read_EMAC_PTP_ISTAT() & RXEL; if (!valid) return; shhwtstamps = skb_hwtstamps(skb); regval = bfin_read_EMAC_PTP_RXSNAPLO(); regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32; ns = timecounter_cyc2time(&lp->clock, regval); timecompare_update(&lp->compare, ns); memset(shhwtstamps, 0, sizeof(*shhwtstamps)); shhwtstamps->hwtstamp = ns_to_ktime(ns); shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns); bfin_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare); }
void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) { struct mlx4_dev *dev = mdev->dev; u64 temp_mult; memset(&mdev->cycles, 0, sizeof(mdev->cycles)); mdev->cycles.read = mlx4_en_read_clock; mdev->cycles.mask = CLOCKSOURCE_MASK(48); /* * we have hca_core_clock in MHz, so to translate cycles to nsecs * we need to divide cycles by freq and multiply by 1000; * in order to get precise result we shift left the value, * since we don't have floating point there; * at the end shift result back */ temp_mult = div_u64(((1ull * 1000) << 29), dev->caps.hca_core_clock); mdev->cycles.mult = (u32)temp_mult; mdev->cycles.shift = 29; timecounter_init(&mdev->clock, &mdev->cycles, ktime_to_ns(ktime_get_real())); memset(&mdev->compare, 0, sizeof(mdev->compare)); mdev->compare.source = &mdev->clock; mdev->compare.target = ktime_get_real; mdev->compare.num_samples = 10; timecompare_update(&mdev->compare, 0); }
static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) { struct bfin_mac_local *lp = netdev_priv(netdev); if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { int timeout_cnt = MAX_TIMEOUT_CNT; skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt)) udelay(1); if (timeout_cnt == 0) netdev_err(netdev, "timestamp the TX packet failed\n"); else { struct skb_shared_hwtstamps shhwtstamps; u64 ns; u64 regval; regval = bfin_read_EMAC_PTP_TXSNAPLO(); regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32; memset(&shhwtstamps, 0, sizeof(shhwtstamps)); ns = timecounter_cyc2time(&lp->clock, regval); timecompare_update(&lp->compare, ns); shhwtstamps.hwtstamp = ns_to_ktime(ns); shhwtstamps.syststamp = timecompare_transform(&lp->compare, ns); skb_tstamp_tx(skb, &shhwtstamps); bfin_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare); } } }
//自定义函数,为结构体timecompare的字段target赋值,函数相当于ktime_t类型变量的构造函数 static ktime_t nstoktime(void) { static ktime_t ktime_zero = { .tv64 = 0 }; //声明变量 ktime_zero.tv64+=jiffies;//为字段tv64赋值 return ktime_zero;//返回变量 } //自定义函数,作为结构体cyclecounter的字段read赋值,作用是获得源时间的时间,在此笔者随意写的 static cycle_t read(const struct cyclecounter *cc) { return cc->mask;//返回字段mask的值 } int __init timecompare_update_init(void) { printk("<0>timecompare_update test begin.\n"); struct cyclecounter cc= { .read=read, .mask=0, .mult=0, .shift=0 }; //声明结构体cyclecounter变量,为结构体timecounter变量的cc字段赋值 struct timecounter tc= { .cc=&cc, .cycle_last=0, .nsec=0 };//声明结构体timecounter变量,为结构体timecompare变量的source字段赋值 struct timecompare sync= { .source=&tc, .target=nstoktime, .num_samples=0, .offset=0, .skew=1, .last_update=1 //当用1初始化时会出现不同的结果 };//函数调用的第一个参数 u64 source_tstamp=0;//声明函数的第二个参数,初始化为0 printk("<0>the old offset is:%lld\n",sync.offset);//显示函数调用之前的offset的值 printk("<0>the old skew is:%lld\n",sync.skew);//显示函数调用之前的skew的值 printk("<0>the old last_update is :%lld\n",sync.last_update);//显示函数调用之前的last_update的值 timecompare_update(&sync,source_tstamp); //调用函数,更改offset和skew字段的值 printk("<0>the new offset is:%lld\n",sync.offset);//显示函数调用之后offset的值 printk("<0>the new skew is:%lld\n",sync.skew);//显示函数调用之后skew的值 printk("<0>the new last_update is :%lld\n",sync.last_update);//显示函数调用之后的last_update的值 printk("<0>timecompare_update test over.\n"); return 0; } void __exit timecompare_update_exit(void) { printk("<1>Goodbye timecompare_update test\n"); } module_init(timecompare_update_init); module_exit(timecompare_update_exit);
static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) { struct bfin_mac_local *lp = netdev_priv(netdev); union skb_shared_tx *shtx = skb_tx(skb); if (shtx->hardware) { int timeout_cnt = MAX_TIMEOUT_CNT; /* When doing time stamping, keep the connection to the socket * a while longer */ shtx->in_progress = 1; /* * The timestamping is done at the EMAC module's MII/RMII interface * when the module sees the Start of Frame of an event message packet. This * interface is the closest possible place to the physical Ethernet transmission * medium, providing the best timing accuracy. */ while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt)) udelay(1); if (timeout_cnt == 0) printk(KERN_ERR DRV_NAME ": fails to timestamp the TX packet\n"); else { struct skb_shared_hwtstamps shhwtstamps; u64 ns; u64 regval; regval = bfin_read_EMAC_PTP_TXSNAPLO(); regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32; memset(&shhwtstamps, 0, sizeof(shhwtstamps)); ns = timecounter_cyc2time(&lp->clock, regval); timecompare_update(&lp->compare, ns); shhwtstamps.hwtstamp = ns_to_ktime(ns); shhwtstamps.syststamp = timecompare_transform(&lp->compare, ns); skb_tstamp_tx(skb, &shhwtstamps); bfin_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare); } } }
void mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev, struct skb_shared_hwtstamps *hwts, u64 timestamp) { u64 nsec; nsec = timecounter_cyc2time(&mdev->clock, timestamp); /* * force a timecompare_update here (even if less than a second * has passed) in order to prevent the case when ptpd or other * software jumps the clock offset. othwerise there is a small * window when the timestamp would be based on previous skew * and invalid results would be pushed to the network stack. */ timecompare_update(&mdev->compare, 0); memset(hwts, 0, sizeof(struct skb_shared_hwtstamps)); hwts->hwtstamp = ns_to_ktime(nsec); hwts->syststamp = timecompare_transform(&mdev->compare, nsec); }
static int bfin_mac_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct hwtstamp_config config; struct bfin_mac_local *lp = netdev_priv(netdev); u16 ptpctl; u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n", __func__, config.flags, config.tx_type, config.rx_filter); if (config.flags) return -EINVAL; if ((config.tx_type != HWTSTAMP_TX_OFF) && (config.tx_type != HWTSTAMP_TX_ON)) return -ERANGE; ptpctl = bfin_read_EMAC_PTP_CTL(); switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: ptpfv3 = 0xFFFFFFFF; bfin_write_EMAC_PTP_FV3(ptpfv3); break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: ptpctl &= ~0x1F00; bfin_write_EMAC_PTP_CTL(ptpctl); ptpfoff = 0x4A24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); ptpfv1 = 0x11040800; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); ptpfv3 = 0xFFFFFFFC; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: ptpctl &= ~0x1F00; bfin_write_EMAC_PTP_CTL(ptpctl); ptpfoff = 0x2A24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); ptpfv1 = 0x11040800; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); ptpfv3 = 0xFFFFFFF0; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: ptpctl &= ~0x1100; bfin_write_EMAC_PTP_CTL(ptpctl); ptpfoff = 0x0E24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); ptpfv1 = 0x110488F7; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); ptpfv3 = 0xFFFFFFF0; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; break; default: return -ERANGE; } if (config.tx_type == HWTSTAMP_TX_OFF && bfin_mac_hwtstamp_is_none(config.rx_filter)) { ptpctl &= ~PTP_EN; bfin_write_EMAC_PTP_CTL(ptpctl); SSYNC(); } else { ptpctl |= PTP_EN; bfin_write_EMAC_PTP_CTL(ptpctl); bfin_read_EMAC_PTP_RXSNAPLO(); bfin_read_EMAC_PTP_RXSNAPHI(); bfin_read_EMAC_PTP_TXSNAPLO(); bfin_read_EMAC_PTP_TXSNAPHI(); bfin_write_EMAC_PTP_TIMELO(0x00000000); bfin_write_EMAC_PTP_TIMEHI(0xFF800000); SSYNC(); lp->compare.last_update = 0; timecounter_init(&lp->clock, &lp->cycles, ktime_to_ns(ktime_get_real())); timecompare_update(&lp->compare, 0); } lp->stamp_cfg = config; return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; }
static int bfin_mac_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct hwtstamp_config config; struct bfin_mac_local *lp = netdev_priv(netdev); u16 ptpctl; u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n", __func__, config.flags, config.tx_type, config.rx_filter); /* reserved for future extensions */ if (config.flags) return -EINVAL; if ((config.tx_type != HWTSTAMP_TX_OFF) && (config.tx_type != HWTSTAMP_TX_ON)) return -ERANGE; ptpctl = bfin_read_EMAC_PTP_CTL(); switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: /* * Dont allow any timestamping */ ptpfv3 = 0xFFFFFFFF; bfin_write_EMAC_PTP_FV3(ptpfv3); break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: /* * Clear the five comparison mask bits (bits[12:8]) in EMAC_PTP_CTL) * to enable all the field matches. */ ptpctl &= ~0x1F00; bfin_write_EMAC_PTP_CTL(ptpctl); /* * Keep the default values of the EMAC_PTP_FOFF register. */ ptpfoff = 0x4A24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); /* * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2 * registers. */ ptpfv1 = 0x11040800; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); /* * The default value (0xFFFC) allows the timestamping of both * received Sync messages and Delay_Req messages. */ ptpfv3 = 0xFFFFFFFC; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: /* Clear all five comparison mask bits (bits[12:8]) in the * EMAC_PTP_CTL register to enable all the field matches. */ ptpctl &= ~0x1F00; bfin_write_EMAC_PTP_CTL(ptpctl); /* * Keep the default values of the EMAC_PTP_FOFF register, except set * the PTPCOF field to 0x2A. */ ptpfoff = 0x2A24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); /* * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2 * registers. */ ptpfv1 = 0x11040800; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); /* * To allow the timestamping of Pdelay_Req and Pdelay_Resp, set * the value to 0xFFF0. */ ptpfv3 = 0xFFFFFFF0; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: /* * Clear bits 8 and 12 of the EMAC_PTP_CTL register to enable only the * EFTM and PTPCM field comparison. */ ptpctl &= ~0x1100; bfin_write_EMAC_PTP_CTL(ptpctl); /* * Keep the default values of all the fields of the EMAC_PTP_FOFF * register, except set the PTPCOF field to 0x0E. */ ptpfoff = 0x0E24170C; bfin_write_EMAC_PTP_FOFF(ptpfoff); /* * Program bits [15:0] of the EMAC_PTP_FV1 register to 0x88F7, which * corresponds to PTP messages on the MAC layer. */ ptpfv1 = 0x110488F7; bfin_write_EMAC_PTP_FV1(ptpfv1); ptpfv2 = 0x0140013F; bfin_write_EMAC_PTP_FV2(ptpfv2); /* * To allow the timestamping of Pdelay_Req and Pdelay_Resp * messages, set the value to 0xFFF0. */ ptpfv3 = 0xFFFFFFF0; bfin_write_EMAC_PTP_FV3(ptpfv3); config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; break; default: return -ERANGE; } if (config.tx_type == HWTSTAMP_TX_OFF && bfin_mac_hwtstamp_is_none(config.rx_filter)) { ptpctl &= ~PTP_EN; bfin_write_EMAC_PTP_CTL(ptpctl); SSYNC(); } else { ptpctl |= PTP_EN; bfin_write_EMAC_PTP_CTL(ptpctl); /* * clear any existing timestamp */ bfin_read_EMAC_PTP_RXSNAPLO(); bfin_read_EMAC_PTP_RXSNAPHI(); bfin_read_EMAC_PTP_TXSNAPLO(); bfin_read_EMAC_PTP_TXSNAPHI(); /* * Set registers so that rollover occurs soon to test this. */ bfin_write_EMAC_PTP_TIMELO(0x00000000); bfin_write_EMAC_PTP_TIMEHI(0xFF800000); SSYNC(); lp->compare.last_update = 0; timecounter_init(&lp->clock, &lp->cycles, ktime_to_ns(ktime_get_real())); timecompare_update(&lp->compare, 0); } lp->stamp_cfg = config; return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; }