Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*******************************************************************************
0003   Copyright (C) 2013  Vayavya Labs Pvt Ltd
0004 
0005   This implements all the API for managing HW timestamp & PTP.
0006 
0007 
0008   Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
0009   Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
0010 *******************************************************************************/
0011 
0012 #include <linux/io.h>
0013 #include <linux/iopoll.h>
0014 #include <linux/delay.h>
0015 #include <linux/ptp_clock_kernel.h>
0016 #include "common.h"
0017 #include "stmmac_ptp.h"
0018 #include "dwmac4.h"
0019 #include "stmmac.h"
0020 
0021 static void config_hw_tstamping(void __iomem *ioaddr, u32 data)
0022 {
0023     writel(data, ioaddr + PTP_TCR);
0024 }
0025 
0026 static void config_sub_second_increment(void __iomem *ioaddr,
0027         u32 ptp_clock, int gmac4, u32 *ssinc)
0028 {
0029     u32 value = readl(ioaddr + PTP_TCR);
0030     unsigned long data;
0031     u32 reg_value;
0032 
0033     /* For GMAC3.x, 4.x versions, in "fine adjustement mode" set sub-second
0034      * increment to twice the number of nanoseconds of a clock cycle.
0035      * The calculation of the default_addend value by the caller will set it
0036      * to mid-range = 2^31 when the remainder of this division is zero,
0037      * which will make the accumulator overflow once every 2 ptp_clock
0038      * cycles, adding twice the number of nanoseconds of a clock cycle :
0039      * 2000000000ULL / ptp_clock.
0040      */
0041     if (value & PTP_TCR_TSCFUPDT)
0042         data = (2000000000ULL / ptp_clock);
0043     else
0044         data = (1000000000ULL / ptp_clock);
0045 
0046     /* 0.465ns accuracy */
0047     if (!(value & PTP_TCR_TSCTRLSSR))
0048         data = (data * 1000) / 465;
0049 
0050     data &= PTP_SSIR_SSINC_MASK;
0051 
0052     reg_value = data;
0053     if (gmac4)
0054         reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
0055 
0056     writel(reg_value, ioaddr + PTP_SSIR);
0057 
0058     if (ssinc)
0059         *ssinc = data;
0060 }
0061 
0062 static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
0063 {
0064     u32 value;
0065 
0066     writel(sec, ioaddr + PTP_STSUR);
0067     writel(nsec, ioaddr + PTP_STNSUR);
0068     /* issue command to initialize the system time value */
0069     value = readl(ioaddr + PTP_TCR);
0070     value |= PTP_TCR_TSINIT;
0071     writel(value, ioaddr + PTP_TCR);
0072 
0073     /* wait for present system time initialize to complete */
0074     return readl_poll_timeout_atomic(ioaddr + PTP_TCR, value,
0075                  !(value & PTP_TCR_TSINIT),
0076                  10, 100000);
0077 }
0078 
0079 static int config_addend(void __iomem *ioaddr, u32 addend)
0080 {
0081     u32 value;
0082     int limit;
0083 
0084     writel(addend, ioaddr + PTP_TAR);
0085     /* issue command to update the addend value */
0086     value = readl(ioaddr + PTP_TCR);
0087     value |= PTP_TCR_TSADDREG;
0088     writel(value, ioaddr + PTP_TCR);
0089 
0090     /* wait for present addend update to complete */
0091     limit = 10;
0092     while (limit--) {
0093         if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
0094             break;
0095         mdelay(10);
0096     }
0097     if (limit < 0)
0098         return -EBUSY;
0099 
0100     return 0;
0101 }
0102 
0103 static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
0104         int add_sub, int gmac4)
0105 {
0106     u32 value;
0107     int limit;
0108 
0109     if (add_sub) {
0110         /* If the new sec value needs to be subtracted with
0111          * the system time, then MAC_STSUR reg should be
0112          * programmed with (2^32 – <new_sec_value>)
0113          */
0114         if (gmac4)
0115             sec = -sec;
0116 
0117         value = readl(ioaddr + PTP_TCR);
0118         if (value & PTP_TCR_TSCTRLSSR)
0119             nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec);
0120         else
0121             nsec = (PTP_BINARY_ROLLOVER_MODE - nsec);
0122     }
0123 
0124     writel(sec, ioaddr + PTP_STSUR);
0125     value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec;
0126     writel(value, ioaddr + PTP_STNSUR);
0127 
0128     /* issue command to initialize the system time value */
0129     value = readl(ioaddr + PTP_TCR);
0130     value |= PTP_TCR_TSUPDT;
0131     writel(value, ioaddr + PTP_TCR);
0132 
0133     /* wait for present system time adjust/update to complete */
0134     limit = 10;
0135     while (limit--) {
0136         if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT))
0137             break;
0138         mdelay(10);
0139     }
0140     if (limit < 0)
0141         return -EBUSY;
0142 
0143     return 0;
0144 }
0145 
0146 static void get_systime(void __iomem *ioaddr, u64 *systime)
0147 {
0148     u64 ns, sec0, sec1;
0149 
0150     /* Get the TSS value */
0151     sec1 = readl_relaxed(ioaddr + PTP_STSR);
0152     do {
0153         sec0 = sec1;
0154         /* Get the TSSS value */
0155         ns = readl_relaxed(ioaddr + PTP_STNSR);
0156         /* Get the TSS value */
0157         sec1 = readl_relaxed(ioaddr + PTP_STSR);
0158     } while (sec0 != sec1);
0159 
0160     if (systime)
0161         *systime = ns + (sec1 * 1000000000ULL);
0162 }
0163 
0164 static void get_ptptime(void __iomem *ptpaddr, u64 *ptp_time)
0165 {
0166     u64 ns;
0167 
0168     ns = readl(ptpaddr + PTP_ATNR);
0169     ns += readl(ptpaddr + PTP_ATSR) * NSEC_PER_SEC;
0170 
0171     *ptp_time = ns;
0172 }
0173 
0174 static void timestamp_interrupt(struct stmmac_priv *priv)
0175 {
0176     u32 num_snapshot, ts_status, tsync_int;
0177     struct ptp_clock_event event;
0178     unsigned long flags;
0179     u64 ptp_time;
0180     int i;
0181 
0182     if (priv->plat->int_snapshot_en) {
0183         wake_up(&priv->tstamp_busy_wait);
0184         return;
0185     }
0186 
0187     tsync_int = readl(priv->ioaddr + GMAC_INT_STATUS) & GMAC_INT_TSIE;
0188 
0189     if (!tsync_int)
0190         return;
0191 
0192     /* Read timestamp status to clear interrupt from either external
0193      * timestamp or start/end of PPS.
0194      */
0195     ts_status = readl(priv->ioaddr + GMAC_TIMESTAMP_STATUS);
0196 
0197     if (!priv->plat->ext_snapshot_en)
0198         return;
0199 
0200     num_snapshot = (ts_status & GMAC_TIMESTAMP_ATSNS_MASK) >>
0201                GMAC_TIMESTAMP_ATSNS_SHIFT;
0202 
0203     for (i = 0; i < num_snapshot; i++) {
0204         read_lock_irqsave(&priv->ptp_lock, flags);
0205         get_ptptime(priv->ptpaddr, &ptp_time);
0206         read_unlock_irqrestore(&priv->ptp_lock, flags);
0207         event.type = PTP_CLOCK_EXTTS;
0208         event.index = 0;
0209         event.timestamp = ptp_time;
0210         ptp_clock_event(priv->ptp_clock, &event);
0211     }
0212 }
0213 
0214 const struct stmmac_hwtimestamp stmmac_ptp = {
0215     .config_hw_tstamping = config_hw_tstamping,
0216     .init_systime = init_systime,
0217     .config_sub_second_increment = config_sub_second_increment,
0218     .config_addend = config_addend,
0219     .adjust_systime = adjust_systime,
0220     .get_systime = get_systime,
0221     .get_ptptime = get_ptptime,
0222     .timestamp_interrupt = timestamp_interrupt,
0223 };