commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=c114b897eb7d47616d607f04e5d9e47bc3c0e1dc branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk
Sample code of ieee1588 implemention for stmmac. Signed-off-by: Bob Liu <[email protected]> --- .../mach-bf609/include/mach/cdefBF60x_base.h | 15 ++ drivers/net/ethernet/stmicro/stmmac/descs.h | 11 + drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 6 +- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 11 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 22 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 260 +++++++++++++++++++- 6 files changed, 320 insertions(+), 5 deletions(-) diff --git a/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h b/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h index c19e754..03982e3 100644 --- a/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h +++ b/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h @@ -49,6 +49,21 @@ /* RTC Registers */ +/* EMAC0 Registers */ +#define bfin_read_EMAC_PTP_ADDEND() bfin_read32(EMAC0_TM_ADDEND, val) +#define bfin_write_EMAC_PTP_ADDEND(val) bfin_write32(EMAC0_TM_ADDEND, val) +#define bfin_read_EMAC_PTP_CTL() bfin_read32(EMAC0_TM_CTL) +#define bfin_write_EMAC_PTP_CTL(val) bfin_write32(EMAC0_TM_CTL, val) +#define bfin_read_EMAC_PTP_SEC() bfin_read32(EMAC0_TM_SEC) +#define bfin_write_EMAC_PTP_SEC(val) bfin_write32(EMAC0_TM_SEC, val) +#define bfin_read_EMAC_PTP_SUBSEC() bfin_read32(EMAC0_TM_SUBSEC) +#define bfin_write_EMAC_PTP_SUBSEC(val) bfin_write32(EMAC0_TM_SUBSEC, val) +#define bfin_read_EMAC_PTP_NSEC() bfin_read32(EMAC0_TM_NSEC) +#define bfin_write_EMAC_PTP_NSEC(val) bfin_write32(EMAC0_TM_NSEC, val) +#define bfin_read_EMAC_PTP_SECUPDT() bfin_read32(EMAC0_TM_SECUPDT) +#define bfin_write_EMAC_PTP_SECUPDT(val) bfin_write32(EMAC0_TM_SECUPDT, val) +#define bfin_read_EMAC_PTP_NSECUPDT() bfin_read32(EMAC0_TM_NSECUPDT) +#define bfin_write_EMAC_PTP_NSECUPDT(val) bfin_write32(EMAC0_TM_NSECUPDT, val) /* UART0 Registers */ diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h index 9820ec8..ffa0073 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs.h @@ -20,6 +20,11 @@ Author: Giuseppe Cavallaro <[email protected]> *******************************************************************************/ + +#ifdef CONFIG_BLACKFIN +#define STMMAC_IEEE1588 +#endif + struct dma_desc { /* Receive descriptor */ union { @@ -156,6 +161,12 @@ struct dma_desc { } des01; unsigned int des2; unsigned int des3; +#ifdef STMMAC_IEEE1588 + unsigned int des4; + unsigned int des5; + unsigned int des6; + unsigned int des7; +#endif }; /* Transmit checksum insertion control */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index d682a0b..6a10c39 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -52,11 +52,13 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx, #ifdef CONFIG_BLACKFIN writel(DMA_AXI_BUS_BLEN4 | DMA_AXI_BUS_UNDEF, ioaddr + DMA_AXI_BUS); -#endif + writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT) | (1 << 7), + ioaddr + DMA_BUS_MODE); +#else /* Enable Application Access by writing to DMA CSR0 */ writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT), ioaddr + DMA_BUS_MODE); - +#endif /* Mask interrupts by writing to CSR7 */ writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index ad1b627..cae3af8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -291,9 +291,16 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, int csum_flag) { p->des01.etx.first_segment = is_fs; - enh_set_tx_desc_len(p, len); - +#ifdef STMMAC_IEEE1588 + p->des01.etx.time_stamp_enable = 1; +#endif + if (unlikely(len > BUF_SIZE_4KiB)) { + p->des01.etx.buffer1_size = BUF_SIZE_4KiB; + p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB; + } else { + p->des01.etx.buffer1_size = len; + } if (likely(csum_flag)) p->des01.etx.checksum_insertion = cic_full; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index b4b095f..fc23d69 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -29,6 +29,22 @@ #include "stmmac_timer.h" #endif +#ifdef STMMAC_IEEE1588 +#include <linux/net_tstamp.h> +#include <linux/clocksource.h> +#include <linux/timecompare.h> +#include <linux/timer.h> + +#define PTP_EN (0x1) /* Enable the PTP_TSYNC module */ +#define PTP_TSINIT (0x4) /* update system timer */ +#define PTP_TSENALL (1 << 8) +#define PTP_TSVER2ENA (1 << 10) +#define PTP_TSIPENA (1 << 11) +#define PTP_TSIPV4ENA (1 << 13) +#define PTP_TSMASTERENA (1 << 15) +#define PTP_TSEVENTENA (1 << 15) +#endif + struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ struct dma_desc *dma_tx ____cacheline_aligned; @@ -81,6 +97,12 @@ struct stmmac_priv { struct stmmac_counters mmc; struct dma_features dma_cap; int hw_cap_support; +#ifdef STMMAC_IEEE1588 + struct cyclecounter cycles; + struct timecounter clock; + struct timecompare compare; + struct hwtstamp_config stamp_cfg; +#endif }; extern int phyaddr; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 0d9b86b..70e4727 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -74,7 +74,12 @@ #define TX_DBG(fmt, args...) do { } while (0) #endif +#ifdef CONFIG_BLACKFIN +#define STMMAC_ALIGN(x) (x) +#else #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) +#endif + #define JUMBO_LEN 9000 /* Module parameters */ @@ -140,6 +145,249 @@ static int stmmac_init_fs(struct net_device *dev); static void stmmac_exit_fs(void); #endif +#ifdef STMMAC_IEEE1588 +#define MAX_TIMEOUT_CNT 5000 +#define stmmac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE) + +static int stmmac_hwtstamp_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct stmmac_priv *lp = netdev_priv(netdev); + u32 ptpctl; + + 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(); + bfin_write_EMAC_PTP_CTL((1 << 16)); + SSYNC(); + ptpctl = bfin_read_EMAC_PTP_CTL(); + printk("ptp init val is: 0x%x, config.rx_filter:0x%x============\n", ptpctl, config.rx_filter); + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + /* + * Dont allow any timestamping + */ + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + ptpctl |= PTP_TSIPV4ENA; /* udp version 1 */ + break; + + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + ptpctl |= (PTP_TSIPV4ENA | PTP_TSVER2ENA); /* udp version 2 */ + break; + + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + ptpctl |= (PTP_TSIPENA | PTP_TSVER2ENA); /* ethernet version 2 */ + break; + + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + ptpctl |= (PTP_TSENALL | PTP_TSVER2ENA); /* ethernet version 2 */ + break; + default: + return -ERANGE; + } + + if (config.tx_type == HWTSTAMP_TX_OFF && + stmmac_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_write_EMAC_PTP_SECUPDT(0x0); + bfin_write_EMAC_PTP_NSECUPDT(0x0); + ptpctl |= PTP_TSINIT; + bfin_write_EMAC_PTP_CTL(ptpctl); + + bfin_write_EMAC_PTP_SUBSEC(0x2b); + + SSYNC(); + + lp->compare.last_update = 0; + timecounter_init(&lp->clock, + &lp->cycles, + ktime_to_ns(ktime_get_real())); + timecompare_update(&lp->compare, 0); + printk("sec %d, nsec %d, ptpctl 0x%x, subsec 0x%x\n", bfin_read_EMAC_PTP_SEC(), + bfin_read_EMAC_PTP_NSEC(), bfin_read_EMAC_PTP_CTL(), bfin_read_EMAC_PTP_SUBSEC()); + } + + lp->stamp_cfg = config; + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static void stmmac_dump_hwtamp(char *s, ktime_t *hw, ktime_t *ts, struct timecompare *cmp) +{ + ktime_t sys = ktime_get_real(); + + printk("%s %s hardware:%d,%d transform system:%d,%d system:%d,%d, cmp:%lld, %lld\n", + __func__, s, hw->tv.sec, hw->tv.nsec, ts->tv.sec, ts->tv.nsec, sys.tv.sec, + sys.tv.nsec, cmp->offset, cmp->skew); +} + +static void stmmac_tx_hwtstamp(struct stmmac_priv *lp, struct sk_buff *skb, + struct dma_desc *desc) +{ + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + int timeout_cnt = MAX_TIMEOUT_CNT; + + /* When doing time stamping, keep the connection to the socket + * a while longer + */ + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + /* + * 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 (!desc->des01.etx.time_stamp_status && (--timeout_cnt)) + udelay(20); + + /* work around */ + /* + if (timeout_cnt == 0) { + printk("%d", desc->des01.etx.time_stamp_status); + if (desc->des01.etx.time_stamp_status) + timeout_cnt = 1; + } + */ + if (timeout_cnt == 0) { + printk("timestamp the TX packet failed==========\n"); + printk("des01.first:%d, tsenable:%d, stamp_status:%d\n", desc->des01.etx.first_segment, + desc->des01.etx.time_stamp_enable, desc->des01.etx.time_stamp_status); + printk("hwsec %d, hwnsec %d\n", bfin_read_EMAC_PTP_SEC(), bfin_read_EMAC_PTP_NSEC()); + if (!desc->des01.etx.time_stamp_status) + return; + } + { + struct skb_shared_hwtstamps shhwtstamps; + u64 ns; + u64 regval; + + regval = desc->des6; + regval |= (u64)desc->des7 << 32; + printk("tx: des06: %ld, des07 %ld\n", desc->des6, desc->des7); + 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); + + stmmac_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare); + } + } +} + +static void stmmac_rx_hwtstamp(struct stmmac_priv *lp, struct sk_buff *skb, struct dma_desc *desc) +{ + u64 regval, ns; + struct skb_shared_hwtstamps *shhwtstamps; + + if (stmmac_hwtstamp_is_none(lp->stamp_cfg.rx_filter)) + return; + + if (!desc->des01.erx.ipc_csum_error) + return; + + shhwtstamps = skb_hwtstamps(skb); + + regval = desc->des6; + regval |= (u64)desc->des7 << 32; + printk("rx: des06: %ld, des07 %ld\n", desc->des6, desc->des7); + 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); + + stmmac_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare); +} + +/* + * stmmac_read_clock - read raw cycle counter (to be used by time counter) + */ +static cycle_t stmmac_read_clock(const struct cyclecounter *tc) +{ + u64 ns; + ktime_t hw_time; + + hw_time.tv.sec = bfin_read_EMAC_PTP_SEC(); + hw_time.tv.nsec = bfin_read_EMAC_PTP_NSEC(); + ns = ktime_to_ns(hw_time); + do_div(ns, 20); + return ns; +} + + +static void stmmac_hwtstamp_init(struct net_device *netdev) +{ + struct stmmac_priv *lp = netdev_priv(netdev); + u64 append; + + *(unsigned int *)(0xFFC03404) = 0x0; + printk("%s===========pads is 0x%x\n", __func__, *(unsigned int *)(0xFFC03404)); + /* Initialize hardware timer */ + + memset(&lp->cycles, 0, sizeof(lp->cycles)); + lp->cycles.read = stmmac_read_clock; + lp->cycles.mask = CLOCKSOURCE_MASK(64); + lp->cycles.mult = 20; + lp->cycles.shift = 0; + + /* Synchronize our NIC clock against system wall clock */ + memset(&lp->compare, 0, sizeof(lp->compare)); + lp->compare.source = &lp->clock; + lp->compare.target = ktime_get_real; + lp->compare.num_samples = 10; + + /* Initialize hwstamp config */ + lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE; + lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF; +} + +#else +# define stmmac_hwtstamp_is_none(cfg) 0 +# define stmmac_hwtstamp_init(dev) +# define stmmac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP) +# define stmmac_rx_hwtstamp(priv, skb, desc) +# define stmmac_tx_hwtstamp(priv, skb, desc) +#endif + /** * stmmac_verify_args - verify the driver parameters. * Description: it verifies if some wrong parameter is passed to the driver. @@ -1200,6 +1448,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->dma->enable_dma_transmission(priv->ioaddr); spin_unlock(&priv->tx_lock); + stmmac_tx_hwtstamp(priv, skb, desc); return NETDEV_TX_OK; } @@ -1314,6 +1563,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) } #endif skb->protocol = eth_type_trans(skb, priv->dev); + stmmac_rx_hwtstamp(priv, skb, p); if (unlikely(!priv->rx_coe)) { /* No RX COE for old mac10/100 devices */ @@ -1492,7 +1742,6 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) priv->hw->mac->host_irq_status((void __iomem *) dev->base_addr); stmmac_dma_interrupt(priv); - return IRQ_HANDLED; } @@ -1528,6 +1777,9 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (!priv->phydev) return -EINVAL; + if (cmd == SIOCSHWTSTAMP) + return stmmac_hwtstamp_ioctl(dev, rq, cmd); + ret = phy_mii_ioctl(priv->phydev, rq, cmd); return ret; @@ -1785,6 +2037,12 @@ static int stmmac_hw_init(struct stmmac_priv *priv) device_set_wakeup_capable(priv->device, 1); } + stmmac_hwtstamp_init(dev); + + DBG(probe, DEBUG, "%s: Scatter/Gather: %s - HW checksums: %s\n", + dev->name, (dev->features & NETIF_F_SG) ? "on" : "off", + (dev->features & NETIF_F_IP_CSUM) ? "on" : "off"); + return ret; }
_______________________________________________ Linux-kernel-commits mailing list [email protected] https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits
