Hi Roger, On 08/08/23 4:24 pm, Roger Quadros wrote: > Hi Danish, > > On 07/08/2023 14:00, MD Danish Anwar wrote: >> From: Roger Quadros <rogerq@xxxxxx> >> >> Add packet timestamping TS and PTP PHC clock support. >> >> For AM65x and AM64x: >> - IEP1 is not used >> - IEP0 is configured in shadow mode with 1ms cycle and shared between >> Linux and FW. It provides time and TS in number cycles, so special >> conversation in ns is required. >> - IEP0 shared between PRUeth ports. >> - IEP0 supports PPS, periodic output. >> - IEP0 settime() and enabling PPS required FW interraction. >> - RX TS provided with each packet in CPPI5 descriptor. >> - TX TS returned through separate ICSSG hw queues for each port. TX TS >> readiness is signaled by INTC IRQ. Only one packet at time can be requested >> for TX TS. >> >> Signed-off-by: Roger Quadros <rogerq@xxxxxx> >> Co-developed-by: Grygorii Strashko <grygorii.strashko@xxxxxx> >> Signed-off-by: Grygorii Strashko <grygorii.strashko@xxxxxx> >> Signed-off-by: Vignesh Raghavendra <vigneshr@xxxxxx> >> Signed-off-by: MD Danish Anwar <danishanwar@xxxxxx> >> --- >> drivers/net/ethernet/ti/icssg/icss_iep.c | 2 +- >> drivers/net/ethernet/ti/icssg/icss_iep.h | 3 +- >> drivers/net/ethernet/ti/icssg/icssg_ethtool.c | 21 + >> drivers/net/ethernet/ti/icssg/icssg_prueth.c | 416 +++++++++++++++++- >> drivers/net/ethernet/ti/icssg/icssg_prueth.h | 28 +- >> 5 files changed, 463 insertions(+), 7 deletions(-) >> >> diff --git a/drivers/net/ethernet/ti/icssg/icss_iep.c b/drivers/net/ethernet/ti/icssg/icss_iep.c >> index cc0ee113a2c5..455c803dea36 100644 >> --- a/drivers/net/ethernet/ti/icssg/icss_iep.c >> +++ b/drivers/net/ethernet/ti/icssg/icss_iep.c >> @@ -224,7 +224,7 @@ static u64 icss_iep_gettime(struct icss_iep *iep, >> unsigned long flags; >> >> if (iep->ops && iep->ops->gettime) >> - return iep->ops->gettime(iep->clockops_data); >> + return iep->ops->gettime(iep->clockops_data, sts); >> >> /* use local_irq_x() to make it work for both RT/non-RT */ >> local_irq_save(flags); >> diff --git a/drivers/net/ethernet/ti/icssg/icss_iep.h b/drivers/net/ethernet/ti/icssg/icss_iep.h >> index 4f9d4b6bb5d1..9c7f4d0a0916 100644 >> --- a/drivers/net/ethernet/ti/icssg/icss_iep.h >> +++ b/drivers/net/ethernet/ti/icssg/icss_iep.h >> @@ -13,12 +13,13 @@ >> #include <linux/regmap.h> >> >> struct icss_iep; >> +extern const struct icss_iep_clockops prueth_iep_clockops; > > Why do you need to do this? Without this I was getting smatch warning to make prueth_iep_clockops as static. > >> >> /* Firmware specific clock operations */ >> struct icss_iep_clockops { >> void (*settime)(void *clockops_data, u64 ns); >> void (*adjtime)(void *clockops_data, s64 delta); >> - u64 (*gettime)(void *clockops_data); >> + u64 (*gettime)(void *clockops_data, struct ptp_system_timestamp *sts); >> int (*perout_enable)(void *clockops_data, >> struct ptp_perout_request *req, int on, >> u64 *cmp); > > > Can we please squash all the above IEP driver changes in the patch that introduces IEP driver? > So this patch only deals with icssg_prueth driver. OK, I will move all changes of icss_iep.c to previous patch. This patch I will modify to have only icssg_prueth changes. > >> diff --git a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c b/drivers/net/ethernet/ti/icssg/icssg_ethtool.c >> index 02c312f01d10..a27ec1dcc8d5 100644 >> --- a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c >> +++ b/drivers/net/ethernet/ti/icssg/icssg_ethtool.c >> @@ -109,6 +109,26 @@ static void emac_get_ethtool_stats(struct net_device *ndev, >> *(data++) = emac->stats[i]; >> } >> >> +static int emac_get_ts_info(struct net_device *ndev, >> + struct ethtool_ts_info *info) >> +{ >> + struct prueth_emac *emac = netdev_priv(ndev); >> + >> + info->so_timestamping = >> + SOF_TIMESTAMPING_TX_HARDWARE | >> + SOF_TIMESTAMPING_TX_SOFTWARE | >> + SOF_TIMESTAMPING_RX_HARDWARE | >> + SOF_TIMESTAMPING_RX_SOFTWARE | >> + SOF_TIMESTAMPING_SOFTWARE | >> + SOF_TIMESTAMPING_RAW_HARDWARE; >> + >> + info->phc_index = icss_iep_get_ptp_clock_idx(emac->iep); >> + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); >> + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); >> + >> + return 0; >> +} >> + >> static int emac_set_channels(struct net_device *ndev, >> struct ethtool_channels *ch) >> { >> @@ -176,6 +196,7 @@ const struct ethtool_ops icssg_ethtool_ops = { >> .get_sset_count = emac_get_sset_count, >> .get_ethtool_stats = emac_get_ethtool_stats, >> .get_strings = emac_get_strings, >> + .get_ts_info = emac_get_ts_info, >> .get_channels = emac_get_channels, >> .set_channels = emac_set_channels, >> .get_link_ksettings = emac_get_link_ksettings, >> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c >> index 47b941fb0198..b82a718fd602 100644 >> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c >> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c >> @@ -56,6 +56,8 @@ >> /* CTRLMMR_ICSSG_RGMII_CTRL register bits */ >> #define ICSSG_CTRL_RGMII_ID_MODE BIT(24) >> >> +#define IEP_DEFAULT_CYCLE_TIME_NS 1000000 /* 1 ms */ >> + >> static void prueth_cleanup_rx_chns(struct prueth_emac *emac, >> struct prueth_rx_chn *rx_chn, >> int max_rflows) >> @@ -471,6 +473,37 @@ static int prueth_dma_rx_push(struct prueth_emac *emac, >> desc_rx, desc_dma); >> } >> >> +static u64 icssg_ts_to_ns(u32 hi_sw, u32 hi, u32 lo, u32 cycle_time_ns) >> +{ >> + u32 iepcount_lo, iepcount_hi, hi_rollover_count; >> + u64 ns; >> + >> + iepcount_lo = lo & GENMASK(19, 0); >> + iepcount_hi = (hi & GENMASK(11, 0)) << 12 | lo >> 20; >> + hi_rollover_count = hi >> 11; >> + >> + ns = ((u64)hi_rollover_count) << 23 | (iepcount_hi + hi_sw); >> + ns = ns * cycle_time_ns + iepcount_lo; >> + >> + return ns; >> +} >> + >> +static void emac_rx_timestamp(struct prueth_emac *emac, >> + struct sk_buff *skb, u32 *psdata) >> +{ >> + struct skb_shared_hwtstamps *ssh; >> + u64 ns; >> + >> + u32 hi_sw = readl(emac->prueth->shram.va + >> + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET); >> + ns = icssg_ts_to_ns(hi_sw, psdata[1], psdata[0], >> + IEP_DEFAULT_CYCLE_TIME_NS); >> + >> + ssh = skb_hwtstamps(skb); >> + memset(ssh, 0, sizeof(*ssh)); >> + ssh->hwtstamp = ns_to_ktime(ns); >> +} >> + >> static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) >> { >> struct prueth_rx_chn *rx_chn = &emac->rx_chns; >> @@ -480,6 +513,7 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) >> struct sk_buff *skb, *new_skb; >> dma_addr_t desc_dma, buf_dma; >> void **swdata; >> + u32 *psdata; >> int ret; >> >> ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_id, &desc_dma); >> @@ -497,6 +531,11 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) >> swdata = cppi5_hdesc_get_swdata(desc_rx); >> skb = *swdata; >> >> + psdata = cppi5_hdesc_get_psdata(desc_rx); >> + /* RX HW timestamp */ >> + if (emac->rx_ts_enabled) >> + emac_rx_timestamp(emac, skb, psdata); >> + >> cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); >> k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); >> pkt_len = cppi5_hdesc_get_pktlen(desc_rx); >> @@ -557,6 +596,82 @@ static void prueth_rx_cleanup(void *data, dma_addr_t desc_dma) >> dev_kfree_skb_any(skb); >> } >> >> +static int emac_get_tx_ts(struct prueth_emac *emac, >> + struct emac_tx_ts_response *rsp) >> +{ >> + struct prueth *prueth = emac->prueth; >> + int slice = prueth_emac_slice(emac); >> + int addr; >> + >> + addr = icssg_queue_pop(prueth, slice == 0 ? >> + ICSSG_TS_POP_SLICE0 : ICSSG_TS_POP_SLICE1); >> + if (addr < 0) >> + return addr; >> + >> + memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp)); >> + /* return buffer back for to pool */ >> + icssg_queue_push(prueth, slice == 0 ? >> + ICSSG_TS_PUSH_SLICE0 : ICSSG_TS_PUSH_SLICE1, addr); >> + >> + return 0; >> +} >> + >> +static void tx_ts_work(struct prueth_emac *emac) >> +{ >> + struct skb_shared_hwtstamps ssh; >> + struct emac_tx_ts_response tsr; >> + struct sk_buff *skb; >> + int timeout = 10; >> + int ret = 0; >> + u32 hi_sw; >> + u64 ns; >> + >> + if (!test_bit(__STATE_TX_TS_IN_PROGRESS, &emac->state)) { >> + netdev_err(emac->ndev, "unexpected TS response\n"); >> + return; >> + } >> + >> + skb = emac->tx_ts_skb; >> + while (timeout-- > 0) { >> + /* wait for response or timeout */ >> + ret = emac_get_tx_ts(emac, &tsr); >> + if (!ret) >> + break; >> + usleep_range(10, 20); >> + } >> + >> + if (ret) { >> + netdev_err(emac->ndev, "TX timestamp timeout\n"); >> + goto error; >> + } >> + >> + if (tsr.cookie != emac->tx_ts_cookie) { >> + netdev_err(emac->ndev, "TX TS cookie mismatch 0x%x:0x%x\n", >> + tsr.cookie, emac->tx_ts_cookie); >> + goto error; >> + } >> + >> + hi_sw = readl(emac->prueth->shram.va + >> + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET); >> + ns = icssg_ts_to_ns(hi_sw, tsr.hi_ts, tsr.lo_ts, >> + IEP_DEFAULT_CYCLE_TIME_NS); >> + >> + emac->tx_ts_cookie++; >> + memset(&ssh, 0, sizeof(ssh)); >> + ssh.hwtstamp = ns_to_ktime(ns); >> + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); >> + >> + skb_tstamp_tx(skb, &ssh); >> + dev_consume_skb_any(skb); >> + >> + return; >> + >> +error: >> + dev_kfree_skb_any(skb); >> + emac->tx_ts_skb = NULL; >> + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); >> +} >> + >> /** >> * emac_ndo_start_xmit - EMAC Transmit function >> * @skb: SKB pointer >> @@ -577,6 +692,7 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> struct prueth_tx_chn *tx_chn; >> dma_addr_t desc_dma, buf_dma; >> int i, ret = 0, q_idx; >> + bool in_tx_ts = 0; >> void **swdata; >> u32 pkt_len; >> u32 *epib; >> @@ -608,6 +724,19 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> epib = first_desc->epib; >> epib[0] = 0; >> epib[1] = 0; >> + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && >> + emac->tx_ts_enabled) { >> + /* We currently support only one TX HW timestamp at a time */ > > If I remember right. There was a patch to get rid of this limitation. > Can you please pick and squash it with this patch? Sure, I will squash it here. I was initially thinking of sending that patch later. But no worries I will squash that patch with this. > >> + if (!test_and_set_bit_lock(__STATE_TX_TS_IN_PROGRESS, >> + &emac->state)) { >> + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; >> + /* Request TX timestamp */ >> + epib[0] = emac->tx_ts_cookie; >> + epib[1] = 0x80000000; /* TX TS request */ >> + emac->tx_ts_skb = skb_get(skb); >> + in_tx_ts = 1; >> + } >> + } >> >> /* set dst tag to indicate internal qid at the firmware which is at >> * bit8..bit15. bit0..bit7 indicates port num for directed >> @@ -629,7 +758,7 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> if (!next_desc) { >> netdev_err(ndev, >> "tx: failed to allocate frag. descriptor\n"); >> - goto free_desc_stop_q_busy; >> + goto free_desc_stop_q_busy_cleanup_tx_ts; >> } >> >> buf_dma = skb_frag_dma_map(tx_chn->dma_dev, frag, 0, frag_size, >> @@ -638,7 +767,7 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> netdev_err(ndev, "tx: Failed to map skb page\n"); >> k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); >> ret = NETDEV_TX_OK; >> - goto drop_free_descs; >> + goto cleanup_tx_ts; >> } >> >> cppi5_hdesc_reset_hbdesc(next_desc); >> @@ -682,6 +811,13 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> >> return NETDEV_TX_OK; >> >> +cleanup_tx_ts: >> + if (in_tx_ts) { >> + dev_kfree_skb_any(emac->tx_ts_skb); >> + emac->tx_ts_skb = NULL; >> + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); >> + } >> + >> drop_free_descs: >> prueth_xmit_free(tx_chn, first_desc); >> >> @@ -694,7 +830,12 @@ static enum netdev_tx emac_ndo_start_xmit(struct sk_buff *skb, struct net_device >> >> return ret; >> >> -free_desc_stop_q_busy: >> +free_desc_stop_q_busy_cleanup_tx_ts: >> + if (in_tx_ts) { >> + dev_kfree_skb_any(emac->tx_ts_skb); >> + emac->tx_ts_skb = NULL; >> + clear_bit_unlock(__STATE_TX_TS_IN_PROGRESS, &emac->state); >> + } >> prueth_xmit_free(tx_chn, first_desc); >> >> drop_stop_q_busy: >> @@ -717,6 +858,16 @@ static void prueth_tx_cleanup(void *data, dma_addr_t desc_dma) >> dev_kfree_skb_any(skb); >> } >> >> +static irqreturn_t prueth_tx_ts_irq(int irq, void *dev_id) >> +{ >> + struct prueth_emac *emac = dev_id; >> + >> + /* currently only TX timestamp is being returned */ >> + tx_ts_work(emac); >> + >> + return IRQ_HANDLED; >> +} >> + >> static irqreturn_t prueth_rx_irq(int irq, void *dev_id) >> { >> struct prueth_emac *emac = dev_id; >> @@ -992,6 +1143,139 @@ static int emac_phy_connect(struct prueth_emac *emac) >> return 0; >> } >> >> +static u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts) >> +{ >> + u32 hi_rollover_count, hi_rollover_count_r; >> + struct prueth_emac *emac = clockops_data; >> + struct prueth *prueth = emac->prueth; >> + void __iomem *fw_hi_r_count_addr; >> + void __iomem *fw_count_hi_addr; >> + u32 iepcount_hi, iepcount_hi_r; >> + unsigned long flags; >> + u32 iepcount_lo; >> + u64 ts = 0; >> + >> + fw_count_hi_addr = prueth->shram.va + TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET; >> + fw_hi_r_count_addr = prueth->shram.va + TIMESYNC_FW_WC_HI_ROLLOVER_COUNT_OFFSET; >> + >> + local_irq_save(flags); >> + do { >> + iepcount_hi = icss_iep_get_count_hi(emac->iep); >> + iepcount_hi += readl(fw_count_hi_addr); >> + hi_rollover_count = readl(fw_hi_r_count_addr); >> + ptp_read_system_prets(sts); >> + iepcount_lo = icss_iep_get_count_low(emac->iep); >> + ptp_read_system_postts(sts); >> + >> + iepcount_hi_r = icss_iep_get_count_hi(emac->iep); >> + iepcount_hi_r += readl(fw_count_hi_addr); >> + hi_rollover_count_r = readl(fw_hi_r_count_addr); >> + } while ((iepcount_hi_r != iepcount_hi) || >> + (hi_rollover_count != hi_rollover_count_r)); >> + local_irq_restore(flags); >> + >> + ts = ((u64)hi_rollover_count) << 23 | iepcount_hi; >> + ts = ts * (u64)IEP_DEFAULT_CYCLE_TIME_NS + iepcount_lo; >> + >> + return ts; >> +} >> + >> +static void prueth_iep_settime(void *clockops_data, u64 ns) >> +{ >> + struct icssg_setclock_desc __iomem *sc_descp; >> + struct prueth_emac *emac = clockops_data; >> + struct icssg_setclock_desc sc_desc; >> + u64 cyclecount; >> + u32 cycletime; >> + int timeout; >> + >> + if (!emac->fw_running) >> + return; >> + >> + sc_descp = emac->prueth->shram.va + TIMESYNC_FW_WC_SETCLOCK_DESC_OFFSET; >> + >> + cycletime = IEP_DEFAULT_CYCLE_TIME_NS; >> + cyclecount = ns / cycletime; >> + >> + memset(&sc_desc, 0, sizeof(sc_desc)); >> + sc_desc.margin = cycletime - 1000; >> + sc_desc.cyclecounter0_set = cyclecount & GENMASK(31, 0); >> + sc_desc.cyclecounter1_set = (cyclecount & GENMASK(63, 32)) >> 32; >> + sc_desc.iepcount_set = ns % cycletime; >> + sc_desc.CMP0_current = cycletime - 4; //Count from 0 to (cycle time)-4 >> + >> + memcpy_toio(sc_descp, &sc_desc, sizeof(sc_desc)); >> + >> + writeb(1, &sc_descp->request); >> + >> + timeout = 5; /* fw should take 2-3 ms */ >> + while (timeout--) { >> + if (readb(&sc_descp->acknowledgment)) >> + return; >> + >> + usleep_range(500, 1000); >> + } >> + >> + dev_err(emac->prueth->dev, "settime timeout\n"); >> +} >> + >> +static int prueth_perout_enable(void *clockops_data, >> + struct ptp_perout_request *req, int on, >> + u64 *cmp) >> +{ >> + struct prueth_emac *emac = clockops_data; >> + u32 reduction_factor = 0, offset = 0; >> + struct timespec64 ts; >> + u64 ns_period; >> + >> + if (!on) >> + return 0; >> + >> + /* Any firmware specific stuff for PPS/PEROUT handling */ >> + ts.tv_sec = req->period.sec; >> + ts.tv_nsec = req->period.nsec; >> + ns_period = timespec64_to_ns(&ts); >> + >> + /* f/w doesn't support period less than cycle time */ >> + if (ns_period < IEP_DEFAULT_CYCLE_TIME_NS) >> + return -ENXIO; >> + >> + reduction_factor = ns_period / IEP_DEFAULT_CYCLE_TIME_NS; >> + offset = ns_period % IEP_DEFAULT_CYCLE_TIME_NS; >> + >> + /* f/w requires at least 1uS within a cycle so CMP >> + * can trigger after SYNC is enabled >> + */ >> + if (offset < 5 * NSEC_PER_USEC) >> + offset = 5 * NSEC_PER_USEC; >> + >> + /* if offset is close to cycle time then we will miss >> + * the CMP event for last tick when IEP rolls over. >> + * In normal mode, IEP tick is 4ns. >> + * In slow compensation it could be 0ns or 8ns at >> + * every slow compensation cycle. >> + */ >> + if (offset > IEP_DEFAULT_CYCLE_TIME_NS - 8) >> + offset = IEP_DEFAULT_CYCLE_TIME_NS - 8; >> + >> + /* we're in shadow mode so need to set upper 32-bits */ >> + *cmp = (u64)offset << 32; >> + >> + writel(reduction_factor, emac->prueth->shram.va + >> + TIMESYNC_FW_WC_SYNCOUT_REDUCTION_FACTOR_OFFSET); >> + >> + writel(0, emac->prueth->shram.va + >> + TIMESYNC_FW_WC_SYNCOUT_START_TIME_CYCLECOUNT_OFFSET); >> + >> + return 0; >> +} >> + >> +const struct icss_iep_clockops prueth_iep_clockops = { >> + .settime = prueth_iep_settime, >> + .gettime = prueth_iep_gettime, >> + .perout_enable = prueth_perout_enable, >> +}; >> + >> /** >> * emac_ndo_open - EMAC device open >> * @ndev: network adapter device >> @@ -1066,10 +1350,20 @@ static int emac_ndo_open(struct net_device *ndev) >> >> icssg_mii_update_mtu(prueth->mii_rt, slice, ndev->max_mtu); >> >> + if (!prueth->emacs_initialized) { >> + ret = icss_iep_init(emac->iep, &prueth_iep_clockops, >> + emac, IEP_DEFAULT_CYCLE_TIME_NS); >> + } >> + >> + ret = request_threaded_irq(emac->tx_ts_irq, NULL, prueth_tx_ts_irq, >> + IRQF_ONESHOT, dev_name(dev), emac); >> + if (ret) >> + goto stop; >> + >> /* Prepare RX */ >> ret = prueth_prepare_rx_chan(emac, &emac->rx_chns, PRUETH_MAX_PKT_SIZE); >> if (ret) >> - goto stop; >> + goto free_tx_ts_irq; >> >> ret = k3_udma_glue_enable_rx_chn(emac->rx_chns.rx_chn); >> if (ret) >> @@ -1102,6 +1396,8 @@ static int emac_ndo_open(struct net_device *ndev) >> prueth_reset_tx_chan(emac, i, false); >> reset_rx_chn: >> prueth_reset_rx_chan(&emac->rx_chns, max_rx_flows, false); >> +free_tx_ts_irq: >> + free_irq(emac->tx_ts_irq, emac); >> stop: >> prueth_emac_stop(emac); >> free_rx_irq: >> @@ -1173,6 +1469,14 @@ static int emac_ndo_stop(struct net_device *ndev) >> /* stop PRUs */ >> prueth_emac_stop(emac); >> >> + if (prueth->emacs_initialized == 1) >> + icss_iep_exit(emac->iep); >> + >> + /* stop PRUs */ >> + prueth_emac_stop(emac); >> + >> + free_irq(emac->tx_ts_irq, emac); >> + >> free_irq(emac->rx_chns.irq[rx_flow], emac); >> prueth_ndev_del_tx_napi(emac, emac->tx_ch_num); >> prueth_cleanup_tx_chns(emac); >> @@ -1235,8 +1539,79 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev) >> queue_work(emac->cmd_wq, &emac->rx_mode_work); >> } >> >> +static int emac_set_ts_config(struct net_device *ndev, struct ifreq *ifr) >> +{ >> + struct prueth_emac *emac = netdev_priv(ndev); >> + struct hwtstamp_config config; >> + >> + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) >> + return -EFAULT; >> + >> + switch (config.tx_type) { >> + case HWTSTAMP_TX_OFF: >> + emac->tx_ts_enabled = 0; >> + break; >> + case HWTSTAMP_TX_ON: >> + emac->tx_ts_enabled = 1; >> + break; >> + default: >> + return -ERANGE; >> + } >> + >> + switch (config.rx_filter) { >> + case HWTSTAMP_FILTER_NONE: >> + emac->rx_ts_enabled = 0; >> + break; >> + case HWTSTAMP_FILTER_ALL: >> + case HWTSTAMP_FILTER_SOME: >> + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: >> + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: >> + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: >> + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: >> + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: >> + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: >> + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: >> + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: >> + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: >> + case HWTSTAMP_FILTER_PTP_V2_EVENT: >> + case HWTSTAMP_FILTER_PTP_V2_SYNC: >> + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: >> + case HWTSTAMP_FILTER_NTP_ALL: >> + emac->rx_ts_enabled = 1; >> + config.rx_filter = HWTSTAMP_FILTER_ALL; >> + break; >> + default: >> + return -ERANGE; >> + } >> + >> + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? >> + -EFAULT : 0; >> +} >> + >> +static int emac_get_ts_config(struct net_device *ndev, struct ifreq *ifr) >> +{ >> + struct prueth_emac *emac = netdev_priv(ndev); >> + struct hwtstamp_config config; >> + >> + config.flags = 0; >> + config.tx_type = emac->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; >> + config.rx_filter = emac->rx_ts_enabled ? HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; >> + >> + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? >> + -EFAULT : 0; >> +} >> + >> static int emac_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) >> { >> + switch (cmd) { >> + case SIOCGHWTSTAMP: >> + return emac_get_ts_config(ndev, ifr); >> + case SIOCSHWTSTAMP: >> + return emac_set_ts_config(ndev, ifr); >> + default: >> + break; >> + } >> + >> return phy_do_ioctl(ndev, ifr, cmd); >> } >> >> @@ -1316,6 +1691,7 @@ static int prueth_netdev_init(struct prueth *prueth, >> struct prueth_emac *emac; >> struct net_device *ndev; >> enum prueth_port port; >> + const char *irq_name; >> enum prueth_mac mac; >> >> port = prueth_node_port(eth_node); >> @@ -1355,6 +1731,15 @@ static int prueth_netdev_init(struct prueth *prueth, >> >> emac->tx_ch_num = 1; >> >> + irq_name = "tx_ts0"; >> + if (emac->port_id == PRUETH_PORT_MII1) >> + irq_name = "tx_ts1"; >> + emac->tx_ts_irq = platform_get_irq_byname_optional(prueth->pdev, irq_name); >> + if (emac->tx_ts_irq < 0) { >> + ret = dev_err_probe(prueth->dev, emac->tx_ts_irq, "could not get tx_ts_irq\n"); >> + goto free; >> + } >> + >> SET_NETDEV_DEV(ndev, prueth->dev); >> spin_lock_init(&emac->lock); >> mutex_init(&emac->cmd_lock); >> @@ -1680,6 +2065,22 @@ static int prueth_probe(struct platform_device *pdev) >> dev_dbg(dev, "sram: pa %llx va %p size %zx\n", prueth->msmcram.pa, >> prueth->msmcram.va, prueth->msmcram.size); >> >> + prueth->iep0 = icss_iep_get_idx(np, 0); >> + if (IS_ERR(prueth->iep0)) { >> + ret = dev_err_probe(dev, PTR_ERR(prueth->iep0), "iep0 get failed\n"); >> + prueth->iep0 = NULL; >> + goto free_pool; >> + } >> + >> + prueth->iep1 = icss_iep_get_idx(np, 1); >> + if (IS_ERR(prueth->iep1)) { >> + ret = dev_err_probe(dev, PTR_ERR(prueth->iep1), "iep1 get failed\n"); >> + icss_iep_put(prueth->iep0); >> + prueth->iep0 = NULL; >> + prueth->iep1 = NULL; >> + goto free_pool; >> + } >> + > > As IEP1 is not used, why do you need to get it? > >> /* setup netdev interfaces */ >> if (eth0_node) { >> ret = prueth_netdev_init(prueth, eth0_node); >> @@ -1688,6 +2089,7 @@ static int prueth_probe(struct platform_device *pdev) >> eth0_node->name); >> goto netdev_exit; >> } >> + prueth->emac[PRUETH_MAC0]->iep = prueth->iep0; >> } >> >> if (eth1_node) { >> @@ -1697,6 +2099,8 @@ static int prueth_probe(struct platform_device *pdev) >> eth1_node->name); >> goto netdev_exit; >> } >> + >> + prueth->emac[PRUETH_MAC1]->iep = prueth->iep0; >> } >> >> /* register the network devices */ >> @@ -1754,6 +2158,7 @@ static int prueth_probe(struct platform_device *pdev) >> prueth_netdev_exit(prueth, eth_node); >> } >> >> +free_pool: >> gen_pool_free(prueth->sram_pool, >> (unsigned long)prueth->msmcram.va, msmc_ram_size); >> >> @@ -1798,6 +2203,9 @@ static void prueth_remove(struct platform_device *pdev) >> prueth_netdev_exit(prueth, eth_node); >> } >> >> + icss_iep_put(prueth->iep1); >> + icss_iep_put(prueth->iep0); >> + >> gen_pool_free(prueth->sram_pool, >> (unsigned long)prueth->msmcram.va, >> MSMC_RAM_SIZE); >> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h >> index a8ce4d01ef16..67ddbff40108 100644 >> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h >> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h >> @@ -35,6 +35,7 @@ >> #include <net/devlink.h> >> >> #include "icssg_config.h" >> +#include "icss_iep.h" >> #include "icssg_switch_map.h" >> >> #define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN) >> @@ -117,6 +118,10 @@ struct prueth_rx_chn { >> char name[32]; >> }; >> >> +enum prueth_state_flags { >> + __STATE_TX_TS_IN_PROGRESS, >> +}; >> + >> /* There are 4 Tx DMA channels, but the highest priority is CH3 (thread 3) >> * and lower three are lower priority channels or threads. >> */ >> @@ -139,6 +144,9 @@ struct prueth_emac { >> struct device_node *phy_node; >> phy_interface_t phy_if; >> enum prueth_port port_id; >> + struct icss_iep *iep; >> + unsigned int rx_ts_enabled : 1; >> + unsigned int tx_ts_enabled : 1; >> >> /* DMA related */ >> struct prueth_tx_chn tx_chns[PRUETH_MAX_TX_QUEUES]; >> @@ -151,6 +159,14 @@ struct prueth_emac { >> spinlock_t lock; /* serialize access */ >> >> unsigned long state; >> + /* TX HW Timestamping */ >> + u32 tx_ts_cookie; >> + struct sk_buff *tx_ts_skb; >> + int tx_ts_irq; >> + >> + u8 cmd_seq; >> + /* shutdown related */ >> + u32 cmd_data[4]; >> struct completion cmd_complete; >> /* Mutex to serialize access to firmware command interface */ >> struct mutex cmd_lock; >> @@ -193,6 +209,8 @@ struct prueth_pdata { >> * @pdata: pointer to platform data for ICSSG driver >> * @icssg_hwcmdseq: seq counter or HWQ messages >> * @emacs_initialized: num of EMACs/ext ports that are up/running >> + * @iep0: pointer to IEP0 device >> + * @iep1: pointer to IEP1 device >> */ >> struct prueth { >> struct device *dev; >> @@ -214,8 +232,16 @@ struct prueth { >> struct platform_device *pdev; >> struct prueth_pdata pdata; >> u8 icssg_hwcmdseq; >> - >> int emacs_initialized; >> + struct icss_iep *iep0; >> + struct icss_iep *iep1; > > Do you need iep1? > IEP1 is needed for 10M FD support. I will move all IEP1 changes to 10M FD patch then. >> +}; >> + >> +struct emac_tx_ts_response { >> + u32 reserved[2]; >> + u32 cookie; >> + u32 lo_ts; >> + u32 hi_ts; >> }; >> >> /* get PRUSS SLICE number from prueth_emac */ > -- Thanks and Regards, Danish.