162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 1588 PTP support for Cadence GEM device. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Cadence Design Systems - https://www.cadence.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Rafal Ozieblo <rafalo@cadence.com> 862306a36Sopenharmony_ci * Bartosz Folta <bfolta@cadence.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/etherdevice.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/time64.h> 1762306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1862306a36Sopenharmony_ci#include <linux/if_ether.h> 1962306a36Sopenharmony_ci#include <linux/if_vlan.h> 2062306a36Sopenharmony_ci#include <linux/net_tstamp.h> 2162306a36Sopenharmony_ci#include <linux/circ_buf.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "macb.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define GEM_PTP_TIMER_NAME "gem-ptp-timer" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp, 2962306a36Sopenharmony_ci struct macb_dma_desc *desc) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (bp->hw_dma_cap == HW_DMA_CAP_PTP) 3262306a36Sopenharmony_ci return (struct macb_dma_desc_ptp *) 3362306a36Sopenharmony_ci ((u8 *)desc + sizeof(struct macb_dma_desc)); 3462306a36Sopenharmony_ci if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP) 3562306a36Sopenharmony_ci return (struct macb_dma_desc_ptp *) 3662306a36Sopenharmony_ci ((u8 *)desc + sizeof(struct macb_dma_desc) 3762306a36Sopenharmony_ci + sizeof(struct macb_dma_desc_64)); 3862306a36Sopenharmony_ci return NULL; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts, 4262306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 4562306a36Sopenharmony_ci unsigned long flags; 4662306a36Sopenharmony_ci long first, second; 4762306a36Sopenharmony_ci u32 secl, sech; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 5062306a36Sopenharmony_ci ptp_read_system_prets(sts); 5162306a36Sopenharmony_ci first = gem_readl(bp, TN); 5262306a36Sopenharmony_ci ptp_read_system_postts(sts); 5362306a36Sopenharmony_ci secl = gem_readl(bp, TSL); 5462306a36Sopenharmony_ci sech = gem_readl(bp, TSH); 5562306a36Sopenharmony_ci second = gem_readl(bp, TN); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* test for nsec rollover */ 5862306a36Sopenharmony_ci if (first > second) { 5962306a36Sopenharmony_ci /* if so, use later read & re-read seconds 6062306a36Sopenharmony_ci * (assume all done within 1s) 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci ptp_read_system_prets(sts); 6362306a36Sopenharmony_ci ts->tv_nsec = gem_readl(bp, TN); 6462306a36Sopenharmony_ci ptp_read_system_postts(sts); 6562306a36Sopenharmony_ci secl = gem_readl(bp, TSL); 6662306a36Sopenharmony_ci sech = gem_readl(bp, TSH); 6762306a36Sopenharmony_ci } else { 6862306a36Sopenharmony_ci ts->tv_nsec = first; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 7262306a36Sopenharmony_ci ts->tv_sec = (((u64)sech << GEM_TSL_SIZE) | secl) 7362306a36Sopenharmony_ci & TSU_SEC_MAX_VAL; 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int gem_tsu_set_time(struct ptp_clock_info *ptp, 7862306a36Sopenharmony_ci const struct timespec64 *ts) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 8162306a36Sopenharmony_ci unsigned long flags; 8262306a36Sopenharmony_ci u32 ns, sech, secl; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci secl = (u32)ts->tv_sec; 8562306a36Sopenharmony_ci sech = (ts->tv_sec >> GEM_TSL_SIZE) & ((1 << GEM_TSH_SIZE) - 1); 8662306a36Sopenharmony_ci ns = ts->tv_nsec; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* TSH doesn't latch the time and no atomicity! */ 9162306a36Sopenharmony_ci gem_writel(bp, TN, 0); /* clear to avoid overflow */ 9262306a36Sopenharmony_ci gem_writel(bp, TSH, sech); 9362306a36Sopenharmony_ci /* write lower bits 2nd, for synchronized secs update */ 9462306a36Sopenharmony_ci gem_writel(bp, TSL, secl); 9562306a36Sopenharmony_ci gem_writel(bp, TN, ns); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci unsigned long flags; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* tsu_timer_incr register must be written after 10762306a36Sopenharmony_ci * the tsu_timer_incr_sub_ns register and the write operation 10862306a36Sopenharmony_ci * will cause the value written to the tsu_timer_incr_sub_ns register 10962306a36Sopenharmony_ci * to take effect. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci spin_lock_irqsave(&bp->tsu_clk_lock, flags); 11262306a36Sopenharmony_ci /* RegBit[15:0] = Subns[23:8]; RegBit[31:24] = Subns[7:0] */ 11362306a36Sopenharmony_ci gem_writel(bp, TISUBN, GEM_BF(SUBNSINCRL, incr_spec->sub_ns) | 11462306a36Sopenharmony_ci GEM_BF(SUBNSINCRH, (incr_spec->sub_ns >> 11562306a36Sopenharmony_ci GEM_SUBNSINCRL_SIZE))); 11662306a36Sopenharmony_ci gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns)); 11762306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 12562306a36Sopenharmony_ci struct tsu_incr incr_spec; 12662306a36Sopenharmony_ci bool neg_adj = false; 12762306a36Sopenharmony_ci u32 word; 12862306a36Sopenharmony_ci u64 adj; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (scaled_ppm < 0) { 13162306a36Sopenharmony_ci neg_adj = true; 13262306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Adjustment is relative to base frequency */ 13662306a36Sopenharmony_ci incr_spec.sub_ns = bp->tsu_incr.sub_ns; 13762306a36Sopenharmony_ci incr_spec.ns = bp->tsu_incr.ns; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ 14062306a36Sopenharmony_ci word = ((u64)incr_spec.ns << GEM_SUBNSINCR_SIZE) + incr_spec.sub_ns; 14162306a36Sopenharmony_ci adj = (u64)scaled_ppm * word; 14262306a36Sopenharmony_ci /* Divide with rounding, equivalent to floating dividing: 14362306a36Sopenharmony_ci * (temp / USEC_PER_SEC) + 0.5 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci adj += (USEC_PER_SEC >> 1); 14662306a36Sopenharmony_ci adj >>= PPM_FRACTION; /* remove fractions */ 14762306a36Sopenharmony_ci adj = div_u64(adj, USEC_PER_SEC); 14862306a36Sopenharmony_ci adj = neg_adj ? (word - adj) : (word + adj); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci incr_spec.ns = (adj >> GEM_SUBNSINCR_SIZE) 15162306a36Sopenharmony_ci & ((1 << GEM_NSINCR_SIZE) - 1); 15262306a36Sopenharmony_ci incr_spec.sub_ns = adj & ((1 << GEM_SUBNSINCR_SIZE) - 1); 15362306a36Sopenharmony_ci gem_tsu_incr_set(bp, &incr_spec); 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); 16062306a36Sopenharmony_ci struct timespec64 now, then = ns_to_timespec64(delta); 16162306a36Sopenharmony_ci u32 adj, sign = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (delta < 0) { 16462306a36Sopenharmony_ci sign = 1; 16562306a36Sopenharmony_ci delta = -delta; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (delta > TSU_NSEC_MAX_VAL) { 16962306a36Sopenharmony_ci gem_tsu_get_time(&bp->ptp_clock_info, &now, NULL); 17062306a36Sopenharmony_ci now = timespec64_add(now, then); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci gem_tsu_set_time(&bp->ptp_clock_info, 17362306a36Sopenharmony_ci (const struct timespec64 *)&now); 17462306a36Sopenharmony_ci } else { 17562306a36Sopenharmony_ci adj = (sign << GEM_ADDSUB_OFFSET) | delta; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci gem_writel(bp, TA, adj); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int gem_ptp_enable(struct ptp_clock_info *ptp, 18462306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return -EOPNOTSUPP; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct ptp_clock_info gem_ptp_caps_template = { 19062306a36Sopenharmony_ci .owner = THIS_MODULE, 19162306a36Sopenharmony_ci .name = GEM_PTP_TIMER_NAME, 19262306a36Sopenharmony_ci .max_adj = 0, 19362306a36Sopenharmony_ci .n_alarm = 0, 19462306a36Sopenharmony_ci .n_ext_ts = 0, 19562306a36Sopenharmony_ci .n_per_out = 0, 19662306a36Sopenharmony_ci .n_pins = 0, 19762306a36Sopenharmony_ci .pps = 1, 19862306a36Sopenharmony_ci .adjfine = gem_ptp_adjfine, 19962306a36Sopenharmony_ci .adjtime = gem_ptp_adjtime, 20062306a36Sopenharmony_ci .gettimex64 = gem_tsu_get_time, 20162306a36Sopenharmony_ci .settime64 = gem_tsu_set_time, 20262306a36Sopenharmony_ci .enable = gem_ptp_enable, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void gem_ptp_init_timer(struct macb *bp) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u32 rem = 0; 20862306a36Sopenharmony_ci u64 adj; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci bp->tsu_incr.ns = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem); 21162306a36Sopenharmony_ci if (rem) { 21262306a36Sopenharmony_ci adj = rem; 21362306a36Sopenharmony_ci adj <<= GEM_SUBNSINCR_SIZE; 21462306a36Sopenharmony_ci bp->tsu_incr.sub_ns = div_u64(adj, bp->tsu_rate); 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci bp->tsu_incr.sub_ns = 0; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void gem_ptp_init_tsu(struct macb *bp) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct timespec64 ts; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 1. get current system time */ 22562306a36Sopenharmony_ci ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* 2. set ptp timer */ 22862306a36Sopenharmony_ci gem_tsu_set_time(&bp->ptp_clock_info, &ts); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 3. set PTP timer increment value to BASE_INCREMENT */ 23162306a36Sopenharmony_ci gem_tsu_incr_set(bp, &bp->tsu_incr); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci gem_writel(bp, TA, 0); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void gem_ptp_clear_timer(struct macb *bp) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci bp->tsu_incr.sub_ns = 0; 23962306a36Sopenharmony_ci bp->tsu_incr.ns = 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0)); 24262306a36Sopenharmony_ci gem_writel(bp, TI, GEM_BF(NSINCR, 0)); 24362306a36Sopenharmony_ci gem_writel(bp, TA, 0); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, 24762306a36Sopenharmony_ci u32 dma_desc_ts_2, struct timespec64 *ts) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct timespec64 tsu; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ts->tv_sec = (GEM_BFEXT(DMA_SECH, dma_desc_ts_2) << GEM_DMA_SECL_SIZE) | 25262306a36Sopenharmony_ci GEM_BFEXT(DMA_SECL, dma_desc_ts_1); 25362306a36Sopenharmony_ci ts->tv_nsec = GEM_BFEXT(DMA_NSEC, dma_desc_ts_1); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* TSU overlapping workaround 25662306a36Sopenharmony_ci * The timestamp only contains lower few bits of seconds, 25762306a36Sopenharmony_ci * so add value from 1588 timer 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci gem_tsu_get_time(&bp->ptp_clock_info, &tsu, NULL); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ts->tv_sec |= ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* If the top bit is set in the timestamp, 26462306a36Sopenharmony_ci * but not in 1588 timer, it has rolled over, 26562306a36Sopenharmony_ci * so subtract max size 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if ((ts->tv_sec & (GEM_DMA_SEC_TOP >> 1)) && 26862306a36Sopenharmony_ci !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) 26962306a36Sopenharmony_ci ts->tv_sec -= GEM_DMA_SEC_TOP; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, 27562306a36Sopenharmony_ci struct macb_dma_desc *desc) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); 27862306a36Sopenharmony_ci struct macb_dma_desc_ptp *desc_ptp; 27962306a36Sopenharmony_ci struct timespec64 ts; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (GEM_BFEXT(DMA_RXVALID, desc->addr)) { 28262306a36Sopenharmony_ci desc_ptp = macb_ptp_desc(bp, desc); 28362306a36Sopenharmony_ci /* Unlikely but check */ 28462306a36Sopenharmony_ci if (!desc_ptp) { 28562306a36Sopenharmony_ci dev_warn_ratelimited(&bp->pdev->dev, 28662306a36Sopenharmony_ci "Timestamp not supported in BD\n"); 28762306a36Sopenharmony_ci return; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); 29062306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); 29162306a36Sopenharmony_ci shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_civoid gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb, 29662306a36Sopenharmony_ci struct macb_dma_desc *desc) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 29962306a36Sopenharmony_ci struct macb_dma_desc_ptp *desc_ptp; 30062306a36Sopenharmony_ci struct timespec64 ts; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) { 30362306a36Sopenharmony_ci dev_warn_ratelimited(&bp->pdev->dev, 30462306a36Sopenharmony_ci "Timestamp not set in TX BD as expected\n"); 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci desc_ptp = macb_ptp_desc(bp, desc); 30962306a36Sopenharmony_ci /* Unlikely but check */ 31062306a36Sopenharmony_ci if (!desc_ptp) { 31162306a36Sopenharmony_ci dev_warn_ratelimited(&bp->pdev->dev, 31262306a36Sopenharmony_ci "Timestamp not supported in BD\n"); 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* ensure ts_1/ts_2 is loaded after ctrl (TX_USED check) */ 31762306a36Sopenharmony_ci dma_rmb(); 31862306a36Sopenharmony_ci gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 32162306a36Sopenharmony_ci shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 32262306a36Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_civoid gem_ptp_init(struct net_device *dev) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct macb *bp = netdev_priv(dev); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci bp->ptp_clock_info = gem_ptp_caps_template; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* nominal frequency and maximum adjustment in ppb */ 33262306a36Sopenharmony_ci bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); 33362306a36Sopenharmony_ci bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj(); 33462306a36Sopenharmony_ci gem_ptp_init_timer(bp); 33562306a36Sopenharmony_ci bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &dev->dev); 33662306a36Sopenharmony_ci if (IS_ERR(bp->ptp_clock)) { 33762306a36Sopenharmony_ci pr_err("ptp clock register failed: %ld\n", 33862306a36Sopenharmony_ci PTR_ERR(bp->ptp_clock)); 33962306a36Sopenharmony_ci bp->ptp_clock = NULL; 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } else if (bp->ptp_clock == NULL) { 34262306a36Sopenharmony_ci pr_err("ptp clock register failed\n"); 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci spin_lock_init(&bp->tsu_clk_lock); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci gem_ptp_init_tsu(bp); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", 35162306a36Sopenharmony_ci GEM_PTP_TIMER_NAME); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_civoid gem_ptp_remove(struct net_device *ndev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct macb *bp = netdev_priv(ndev); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (bp->ptp_clock) 35962306a36Sopenharmony_ci ptp_clock_unregister(bp->ptp_clock); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci gem_ptp_clear_timer(bp); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n", 36462306a36Sopenharmony_ci GEM_PTP_TIMER_NAME); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int gem_ptp_set_ts_mode(struct macb *bp, 36862306a36Sopenharmony_ci enum macb_bd_control tx_bd_control, 36962306a36Sopenharmony_ci enum macb_bd_control rx_bd_control) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci gem_writel(bp, TXBDCTRL, GEM_BF(TXTSMODE, tx_bd_control)); 37262306a36Sopenharmony_ci gem_writel(bp, RXBDCTRL, GEM_BF(RXTSMODE, rx_bd_control)); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciint gem_get_hwtst(struct net_device *dev, struct ifreq *rq) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct hwtstamp_config *tstamp_config; 38062306a36Sopenharmony_ci struct macb *bp = netdev_priv(dev); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci tstamp_config = &bp->tstamp_config; 38362306a36Sopenharmony_ci if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) 38462306a36Sopenharmony_ci return -EOPNOTSUPP; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) 38762306a36Sopenharmony_ci return -EFAULT; 38862306a36Sopenharmony_ci else 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci u32 reg_val; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci reg_val = macb_readl(bp, NCR); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (enable) 39962306a36Sopenharmony_ci macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE)); 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciint gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; 40762306a36Sopenharmony_ci enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; 40862306a36Sopenharmony_ci struct hwtstamp_config *tstamp_config; 40962306a36Sopenharmony_ci struct macb *bp = netdev_priv(dev); 41062306a36Sopenharmony_ci u32 regval; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci tstamp_config = &bp->tstamp_config; 41362306a36Sopenharmony_ci if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) 41462306a36Sopenharmony_ci return -EOPNOTSUPP; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (copy_from_user(tstamp_config, ifr->ifr_data, 41762306a36Sopenharmony_ci sizeof(*tstamp_config))) 41862306a36Sopenharmony_ci return -EFAULT; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci switch (tstamp_config->tx_type) { 42162306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 42462306a36Sopenharmony_ci gem_ptp_set_one_step_sync(bp, 1); 42562306a36Sopenharmony_ci tx_bd_control = TSTAMP_ALL_FRAMES; 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 42862306a36Sopenharmony_ci gem_ptp_set_one_step_sync(bp, 0); 42962306a36Sopenharmony_ci tx_bd_control = TSTAMP_ALL_FRAMES; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci default: 43262306a36Sopenharmony_ci return -ERANGE; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci switch (tstamp_config->rx_filter) { 43662306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 44362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 44462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 44562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 44662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 44762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 44862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 44962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 45062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 45162306a36Sopenharmony_ci rx_bd_control = TSTAMP_ALL_PTP_FRAMES; 45262306a36Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 45362306a36Sopenharmony_ci regval = macb_readl(bp, NCR); 45462306a36Sopenharmony_ci macb_writel(bp, NCR, (regval | MACB_BIT(SRTSM))); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 45762306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 45862306a36Sopenharmony_ci rx_bd_control = TSTAMP_ALL_FRAMES; 45962306a36Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci default: 46262306a36Sopenharmony_ci tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE; 46362306a36Sopenharmony_ci return -ERANGE; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) 46762306a36Sopenharmony_ci return -ERANGE; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) 47062306a36Sopenharmony_ci return -EFAULT; 47162306a36Sopenharmony_ci else 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 475