18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * 1588 PTP support for Cadence GEM device. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Cadence Design Systems - https://www.cadence.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Rafal Ozieblo <rafalo@cadence.com> 88c2ecf20Sopenharmony_ci * Bartosz Folta <bfolta@cadence.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/time64.h> 178c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 188c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 198c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 208c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 218c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "macb.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define GEM_PTP_TIMER_NAME "gem-ptp-timer" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp, 298c2ecf20Sopenharmony_ci struct macb_dma_desc *desc) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (bp->hw_dma_cap == HW_DMA_CAP_PTP) 328c2ecf20Sopenharmony_ci return (struct macb_dma_desc_ptp *) 338c2ecf20Sopenharmony_ci ((u8 *)desc + sizeof(struct macb_dma_desc)); 348c2ecf20Sopenharmony_ci if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP) 358c2ecf20Sopenharmony_ci return (struct macb_dma_desc_ptp *) 368c2ecf20Sopenharmony_ci ((u8 *)desc + sizeof(struct macb_dma_desc) 378c2ecf20Sopenharmony_ci + sizeof(struct macb_dma_desc_64)); 388c2ecf20Sopenharmony_ci return NULL; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci long first, second; 468c2ecf20Sopenharmony_ci u32 secl, sech; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 498c2ecf20Sopenharmony_ci first = gem_readl(bp, TN); 508c2ecf20Sopenharmony_ci secl = gem_readl(bp, TSL); 518c2ecf20Sopenharmony_ci sech = gem_readl(bp, TSH); 528c2ecf20Sopenharmony_ci second = gem_readl(bp, TN); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* test for nsec rollover */ 558c2ecf20Sopenharmony_ci if (first > second) { 568c2ecf20Sopenharmony_ci /* if so, use later read & re-read seconds 578c2ecf20Sopenharmony_ci * (assume all done within 1s) 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci ts->tv_nsec = gem_readl(bp, TN); 608c2ecf20Sopenharmony_ci secl = gem_readl(bp, TSL); 618c2ecf20Sopenharmony_ci sech = gem_readl(bp, TSH); 628c2ecf20Sopenharmony_ci } else { 638c2ecf20Sopenharmony_ci ts->tv_nsec = first; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 678c2ecf20Sopenharmony_ci ts->tv_sec = (((u64)sech << GEM_TSL_SIZE) | secl) 688c2ecf20Sopenharmony_ci & TSU_SEC_MAX_VAL; 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int gem_tsu_set_time(struct ptp_clock_info *ptp, 738c2ecf20Sopenharmony_ci const struct timespec64 *ts) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci u32 ns, sech, secl; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci secl = (u32)ts->tv_sec; 808c2ecf20Sopenharmony_ci sech = (ts->tv_sec >> GEM_TSL_SIZE) & ((1 << GEM_TSH_SIZE) - 1); 818c2ecf20Sopenharmony_ci ns = ts->tv_nsec; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* TSH doesn't latch the time and no atomicity! */ 868c2ecf20Sopenharmony_ci gem_writel(bp, TN, 0); /* clear to avoid overflow */ 878c2ecf20Sopenharmony_ci gem_writel(bp, TSH, sech); 888c2ecf20Sopenharmony_ci /* write lower bits 2nd, for synchronized secs update */ 898c2ecf20Sopenharmony_ci gem_writel(bp, TSL, secl); 908c2ecf20Sopenharmony_ci gem_writel(bp, TN, ns); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci unsigned long flags; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* tsu_timer_incr register must be written after 1028c2ecf20Sopenharmony_ci * the tsu_timer_incr_sub_ns register and the write operation 1038c2ecf20Sopenharmony_ci * will cause the value written to the tsu_timer_incr_sub_ns register 1048c2ecf20Sopenharmony_ci * to take effect. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 1078c2ecf20Sopenharmony_ci /* RegBit[15:0] = Subns[23:8]; RegBit[31:24] = Subns[7:0] */ 1088c2ecf20Sopenharmony_ci gem_writel(bp, TISUBN, GEM_BF(SUBNSINCRL, incr_spec->sub_ns) | 1098c2ecf20Sopenharmony_ci GEM_BF(SUBNSINCRH, (incr_spec->sub_ns >> 1108c2ecf20Sopenharmony_ci GEM_SUBNSINCRL_SIZE))); 1118c2ecf20Sopenharmony_ci gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns)); 1128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 1208c2ecf20Sopenharmony_ci struct tsu_incr incr_spec; 1218c2ecf20Sopenharmony_ci bool neg_adj = false; 1228c2ecf20Sopenharmony_ci u32 word; 1238c2ecf20Sopenharmony_ci u64 adj; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 1268c2ecf20Sopenharmony_ci neg_adj = true; 1278c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Adjustment is relative to base frequency */ 1318c2ecf20Sopenharmony_ci incr_spec.sub_ns = bp->tsu_incr.sub_ns; 1328c2ecf20Sopenharmony_ci incr_spec.ns = bp->tsu_incr.ns; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ 1358c2ecf20Sopenharmony_ci word = ((u64)incr_spec.ns << GEM_SUBNSINCR_SIZE) + incr_spec.sub_ns; 1368c2ecf20Sopenharmony_ci adj = (u64)scaled_ppm * word; 1378c2ecf20Sopenharmony_ci /* Divide with rounding, equivalent to floating dividing: 1388c2ecf20Sopenharmony_ci * (temp / USEC_PER_SEC) + 0.5 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci adj += (USEC_PER_SEC >> 1); 1418c2ecf20Sopenharmony_ci adj >>= PPM_FRACTION; /* remove fractions */ 1428c2ecf20Sopenharmony_ci adj = div_u64(adj, USEC_PER_SEC); 1438c2ecf20Sopenharmony_ci adj = neg_adj ? (word - adj) : (word + adj); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci incr_spec.ns = (adj >> GEM_SUBNSINCR_SIZE) 1468c2ecf20Sopenharmony_ci & ((1 << GEM_NSINCR_SIZE) - 1); 1478c2ecf20Sopenharmony_ci incr_spec.sub_ns = adj & ((1 << GEM_SUBNSINCR_SIZE) - 1); 1488c2ecf20Sopenharmony_ci gem_tsu_incr_set(bp, &incr_spec); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 1558c2ecf20Sopenharmony_ci struct timespec64 now, then = ns_to_timespec64(delta); 1568c2ecf20Sopenharmony_ci u32 adj, sign = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (delta < 0) { 1598c2ecf20Sopenharmony_ci sign = 1; 1608c2ecf20Sopenharmony_ci delta = -delta; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (delta > TSU_NSEC_MAX_VAL) { 1648c2ecf20Sopenharmony_ci gem_tsu_get_time(&bp->ptp_clock_info, &now); 1658c2ecf20Sopenharmony_ci now = timespec64_add(now, then); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci gem_tsu_set_time(&bp->ptp_clock_info, 1688c2ecf20Sopenharmony_ci (const struct timespec64 *)&now); 1698c2ecf20Sopenharmony_ci } else { 1708c2ecf20Sopenharmony_ci adj = (sign << GEM_ADDSUB_OFFSET) | delta; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci gem_writel(bp, TA, adj); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int gem_ptp_enable(struct ptp_clock_info *ptp, 1798c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct ptp_clock_info gem_ptp_caps_template = { 1858c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1868c2ecf20Sopenharmony_ci .name = GEM_PTP_TIMER_NAME, 1878c2ecf20Sopenharmony_ci .max_adj = 0, 1888c2ecf20Sopenharmony_ci .n_alarm = 0, 1898c2ecf20Sopenharmony_ci .n_ext_ts = 0, 1908c2ecf20Sopenharmony_ci .n_per_out = 0, 1918c2ecf20Sopenharmony_ci .n_pins = 0, 1928c2ecf20Sopenharmony_ci .pps = 1, 1938c2ecf20Sopenharmony_ci .adjfine = gem_ptp_adjfine, 1948c2ecf20Sopenharmony_ci .adjtime = gem_ptp_adjtime, 1958c2ecf20Sopenharmony_ci .gettime64 = gem_tsu_get_time, 1968c2ecf20Sopenharmony_ci .settime64 = gem_tsu_set_time, 1978c2ecf20Sopenharmony_ci .enable = gem_ptp_enable, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void gem_ptp_init_timer(struct macb *bp) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u32 rem = 0; 2038c2ecf20Sopenharmony_ci u64 adj; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci bp->tsu_incr.ns = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem); 2068c2ecf20Sopenharmony_ci if (rem) { 2078c2ecf20Sopenharmony_ci adj = rem; 2088c2ecf20Sopenharmony_ci adj <<= GEM_SUBNSINCR_SIZE; 2098c2ecf20Sopenharmony_ci bp->tsu_incr.sub_ns = div_u64(adj, bp->tsu_rate); 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci bp->tsu_incr.sub_ns = 0; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void gem_ptp_init_tsu(struct macb *bp) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct timespec64 ts; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 1. get current system time */ 2208c2ecf20Sopenharmony_ci ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* 2. set ptp timer */ 2238c2ecf20Sopenharmony_ci gem_tsu_set_time(&bp->ptp_clock_info, &ts); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 3. set PTP timer increment value to BASE_INCREMENT */ 2268c2ecf20Sopenharmony_ci gem_tsu_incr_set(bp, &bp->tsu_incr); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci gem_writel(bp, TA, 0); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void gem_ptp_clear_timer(struct macb *bp) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci bp->tsu_incr.sub_ns = 0; 2348c2ecf20Sopenharmony_ci bp->tsu_incr.ns = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0)); 2378c2ecf20Sopenharmony_ci gem_writel(bp, TI, GEM_BF(NSINCR, 0)); 2388c2ecf20Sopenharmony_ci gem_writel(bp, TA, 0); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, 2428c2ecf20Sopenharmony_ci u32 dma_desc_ts_2, struct timespec64 *ts) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct timespec64 tsu; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ts->tv_sec = (GEM_BFEXT(DMA_SECH, dma_desc_ts_2) << GEM_DMA_SECL_SIZE) | 2478c2ecf20Sopenharmony_ci GEM_BFEXT(DMA_SECL, dma_desc_ts_1); 2488c2ecf20Sopenharmony_ci ts->tv_nsec = GEM_BFEXT(DMA_NSEC, dma_desc_ts_1); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* TSU overlapping workaround 2518c2ecf20Sopenharmony_ci * The timestamp only contains lower few bits of seconds, 2528c2ecf20Sopenharmony_ci * so add value from 1588 timer 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci gem_tsu_get_time(&bp->ptp_clock_info, &tsu); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* If the top bit is set in the timestamp, 2578c2ecf20Sopenharmony_ci * but not in 1588 timer, it has rolled over, 2588c2ecf20Sopenharmony_ci * so subtract max size 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci if ((ts->tv_sec & (GEM_DMA_SEC_TOP >> 1)) && 2618c2ecf20Sopenharmony_ci !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) 2628c2ecf20Sopenharmony_ci ts->tv_sec -= GEM_DMA_SEC_TOP; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ts->tv_sec += ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, 2708c2ecf20Sopenharmony_ci struct macb_dma_desc *desc) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); 2738c2ecf20Sopenharmony_ci struct macb_dma_desc_ptp *desc_ptp; 2748c2ecf20Sopenharmony_ci struct timespec64 ts; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (GEM_BFEXT(DMA_RXVALID, desc->addr)) { 2778c2ecf20Sopenharmony_ci desc_ptp = macb_ptp_desc(bp, desc); 2788c2ecf20Sopenharmony_ci /* Unlikely but check */ 2798c2ecf20Sopenharmony_ci if (!desc_ptp) { 2808c2ecf20Sopenharmony_ci dev_warn_ratelimited(&bp->pdev->dev, 2818c2ecf20Sopenharmony_ci "Timestamp not supported in BD\n"); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); 2858c2ecf20Sopenharmony_ci memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); 2868c2ecf20Sopenharmony_ci shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb, 2918c2ecf20Sopenharmony_ci struct macb_dma_desc_ptp *desc_ptp) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 2948c2ecf20Sopenharmony_ci struct timespec64 ts; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); 2978c2ecf20Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 2988c2ecf20Sopenharmony_ci shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 2998c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciint gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, 3038c2ecf20Sopenharmony_ci struct macb_dma_desc *desc) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned long tail = READ_ONCE(queue->tx_ts_tail); 3068c2ecf20Sopenharmony_ci unsigned long head = queue->tx_ts_head; 3078c2ecf20Sopenharmony_ci struct macb_dma_desc_ptp *desc_ptp; 3088c2ecf20Sopenharmony_ci struct gem_tx_ts *tx_timestamp; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0) 3148c2ecf20Sopenharmony_ci return -ENOMEM; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci desc_ptp = macb_ptp_desc(queue->bp, desc); 3178c2ecf20Sopenharmony_ci /* Unlikely but check */ 3188c2ecf20Sopenharmony_ci if (!desc_ptp) 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 3218c2ecf20Sopenharmony_ci tx_timestamp = &queue->tx_timestamps[head]; 3228c2ecf20Sopenharmony_ci tx_timestamp->skb = skb; 3238c2ecf20Sopenharmony_ci /* ensure ts_1/ts_2 is loaded after ctrl (TX_USED check) */ 3248c2ecf20Sopenharmony_ci dma_rmb(); 3258c2ecf20Sopenharmony_ci tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1; 3268c2ecf20Sopenharmony_ci tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2; 3278c2ecf20Sopenharmony_ci /* move head */ 3288c2ecf20Sopenharmony_ci smp_store_release(&queue->tx_ts_head, 3298c2ecf20Sopenharmony_ci (head + 1) & (PTP_TS_BUFFER_SIZE - 1)); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci schedule_work(&queue->tx_ts_task); 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void gem_tx_timestamp_flush(struct work_struct *work) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct macb_queue *queue = 3388c2ecf20Sopenharmony_ci container_of(work, struct macb_queue, tx_ts_task); 3398c2ecf20Sopenharmony_ci unsigned long head, tail; 3408c2ecf20Sopenharmony_ci struct gem_tx_ts *tx_ts; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* take current head */ 3438c2ecf20Sopenharmony_ci head = smp_load_acquire(&queue->tx_ts_head); 3448c2ecf20Sopenharmony_ci tail = queue->tx_ts_tail; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) { 3478c2ecf20Sopenharmony_ci tx_ts = &queue->tx_timestamps[tail]; 3488c2ecf20Sopenharmony_ci gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp); 3498c2ecf20Sopenharmony_ci /* cleanup */ 3508c2ecf20Sopenharmony_ci dev_kfree_skb_any(tx_ts->skb); 3518c2ecf20Sopenharmony_ci /* remove old tail */ 3528c2ecf20Sopenharmony_ci smp_store_release(&queue->tx_ts_tail, 3538c2ecf20Sopenharmony_ci (tail + 1) & (PTP_TS_BUFFER_SIZE - 1)); 3548c2ecf20Sopenharmony_ci tail = queue->tx_ts_tail; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_civoid gem_ptp_init(struct net_device *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct macb *bp = netdev_priv(dev); 3618c2ecf20Sopenharmony_ci struct macb_queue *queue; 3628c2ecf20Sopenharmony_ci unsigned int q; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci bp->ptp_clock_info = gem_ptp_caps_template; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* nominal frequency and maximum adjustment in ppb */ 3678c2ecf20Sopenharmony_ci bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); 3688c2ecf20Sopenharmony_ci bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj(); 3698c2ecf20Sopenharmony_ci gem_ptp_init_timer(bp); 3708c2ecf20Sopenharmony_ci bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &dev->dev); 3718c2ecf20Sopenharmony_ci if (IS_ERR(bp->ptp_clock)) { 3728c2ecf20Sopenharmony_ci pr_err("ptp clock register failed: %ld\n", 3738c2ecf20Sopenharmony_ci PTR_ERR(bp->ptp_clock)); 3748c2ecf20Sopenharmony_ci bp->ptp_clock = NULL; 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci } else if (bp->ptp_clock == NULL) { 3778c2ecf20Sopenharmony_ci pr_err("ptp clock register failed\n"); 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci spin_lock_init(&bp->tsu_clk_lock); 3828c2ecf20Sopenharmony_ci for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { 3838c2ecf20Sopenharmony_ci queue->tx_ts_head = 0; 3848c2ecf20Sopenharmony_ci queue->tx_ts_tail = 0; 3858c2ecf20Sopenharmony_ci INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci gem_ptp_init_tsu(bp); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", 3918c2ecf20Sopenharmony_ci GEM_PTP_TIMER_NAME); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_civoid gem_ptp_remove(struct net_device *ndev) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct macb *bp = netdev_priv(ndev); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (bp->ptp_clock) 3998c2ecf20Sopenharmony_ci ptp_clock_unregister(bp->ptp_clock); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci gem_ptp_clear_timer(bp); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n", 4048c2ecf20Sopenharmony_ci GEM_PTP_TIMER_NAME); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int gem_ptp_set_ts_mode(struct macb *bp, 4088c2ecf20Sopenharmony_ci enum macb_bd_control tx_bd_control, 4098c2ecf20Sopenharmony_ci enum macb_bd_control rx_bd_control) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci gem_writel(bp, TXBDCTRL, GEM_BF(TXTSMODE, tx_bd_control)); 4128c2ecf20Sopenharmony_ci gem_writel(bp, RXBDCTRL, GEM_BF(RXTSMODE, rx_bd_control)); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ciint gem_get_hwtst(struct net_device *dev, struct ifreq *rq) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct hwtstamp_config *tstamp_config; 4208c2ecf20Sopenharmony_ci struct macb *bp = netdev_priv(dev); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci tstamp_config = &bp->tstamp_config; 4238c2ecf20Sopenharmony_ci if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) 4248c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) 4278c2ecf20Sopenharmony_ci return -EFAULT; 4288c2ecf20Sopenharmony_ci else 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci u32 reg_val; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci reg_val = macb_readl(bp, NCR); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (enable) 4398c2ecf20Sopenharmony_ci macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE)); 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciint gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; 4498c2ecf20Sopenharmony_ci enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; 4508c2ecf20Sopenharmony_ci struct hwtstamp_config *tstamp_config; 4518c2ecf20Sopenharmony_ci struct macb *bp = netdev_priv(dev); 4528c2ecf20Sopenharmony_ci u32 regval; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci tstamp_config = &bp->tstamp_config; 4558c2ecf20Sopenharmony_ci if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) 4568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (copy_from_user(tstamp_config, ifr->ifr_data, 4598c2ecf20Sopenharmony_ci sizeof(*tstamp_config))) 4608c2ecf20Sopenharmony_ci return -EFAULT; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* reserved for future extensions */ 4638c2ecf20Sopenharmony_ci if (tstamp_config->flags) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci switch (tstamp_config->tx_type) { 4678c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 4708c2ecf20Sopenharmony_ci if (gem_ptp_set_one_step_sync(bp, 1) != 0) 4718c2ecf20Sopenharmony_ci return -ERANGE; 4728c2ecf20Sopenharmony_ci fallthrough; 4738c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 4748c2ecf20Sopenharmony_ci tx_bd_control = TSTAMP_ALL_FRAMES; 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci default: 4778c2ecf20Sopenharmony_ci return -ERANGE; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci switch (tstamp_config->rx_filter) { 4818c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 4868c2ecf20Sopenharmony_ci break; 4878c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 4888c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 4898c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 4908c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 4918c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 4928c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 4938c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 4948c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 4958c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 4968c2ecf20Sopenharmony_ci rx_bd_control = TSTAMP_ALL_PTP_FRAMES; 4978c2ecf20Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 4988c2ecf20Sopenharmony_ci regval = macb_readl(bp, NCR); 4998c2ecf20Sopenharmony_ci macb_writel(bp, NCR, (regval | MACB_BIT(SRTSM))); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 5028c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 5038c2ecf20Sopenharmony_ci rx_bd_control = TSTAMP_ALL_FRAMES; 5048c2ecf20Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci default: 5078c2ecf20Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE; 5088c2ecf20Sopenharmony_ci return -ERANGE; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) 5128c2ecf20Sopenharmony_ci return -ERANGE; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) 5158c2ecf20Sopenharmony_ci return -EFAULT; 5168c2ecf20Sopenharmony_ci else 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 520