linux/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*******************************************************************************
   3  Copyright (C) 2013  Vayavya Labs Pvt Ltd
   4
   5  This implements all the API for managing HW timestamp & PTP.
   6
   7
   8  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
   9  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
  10*******************************************************************************/
  11
  12#include <linux/io.h>
  13#include <linux/iopoll.h>
  14#include <linux/delay.h>
  15#include <linux/ptp_clock_kernel.h>
  16#include "common.h"
  17#include "stmmac_ptp.h"
  18#include "dwmac4.h"
  19#include "stmmac.h"
  20
  21static void config_hw_tstamping(void __iomem *ioaddr, u32 data)
  22{
  23        writel(data, ioaddr + PTP_TCR);
  24}
  25
  26static void config_sub_second_increment(void __iomem *ioaddr,
  27                u32 ptp_clock, int gmac4, u32 *ssinc)
  28{
  29        u32 value = readl(ioaddr + PTP_TCR);
  30        unsigned long data;
  31        u32 reg_value;
  32
  33        /* For GMAC3.x, 4.x versions, in "fine adjustement mode" set sub-second
  34         * increment to twice the number of nanoseconds of a clock cycle.
  35         * The calculation of the default_addend value by the caller will set it
  36         * to mid-range = 2^31 when the remainder of this division is zero,
  37         * which will make the accumulator overflow once every 2 ptp_clock
  38         * cycles, adding twice the number of nanoseconds of a clock cycle :
  39         * 2000000000ULL / ptp_clock.
  40         */
  41        if (value & PTP_TCR_TSCFUPDT)
  42                data = (2000000000ULL / ptp_clock);
  43        else
  44                data = (1000000000ULL / ptp_clock);
  45
  46        /* 0.465ns accuracy */
  47        if (!(value & PTP_TCR_TSCTRLSSR))
  48                data = (data * 1000) / 465;
  49
  50        data &= PTP_SSIR_SSINC_MASK;
  51
  52        reg_value = data;
  53        if (gmac4)
  54                reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
  55
  56        writel(reg_value, ioaddr + PTP_SSIR);
  57
  58        if (ssinc)
  59                *ssinc = data;
  60}
  61
  62static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
  63{
  64        u32 value;
  65
  66        writel(sec, ioaddr + PTP_STSUR);
  67        writel(nsec, ioaddr + PTP_STNSUR);
  68        /* issue command to initialize the system time value */
  69        value = readl(ioaddr + PTP_TCR);
  70        value |= PTP_TCR_TSINIT;
  71        writel(value, ioaddr + PTP_TCR);
  72
  73        /* wait for present system time initialize to complete */
  74        return readl_poll_timeout(ioaddr + PTP_TCR, value,
  75                                 !(value & PTP_TCR_TSINIT),
  76                                 10000, 100000);
  77}
  78
  79static int config_addend(void __iomem *ioaddr, u32 addend)
  80{
  81        u32 value;
  82        int limit;
  83
  84        writel(addend, ioaddr + PTP_TAR);
  85        /* issue command to update the addend value */
  86        value = readl(ioaddr + PTP_TCR);
  87        value |= PTP_TCR_TSADDREG;
  88        writel(value, ioaddr + PTP_TCR);
  89
  90        /* wait for present addend update to complete */
  91        limit = 10;
  92        while (limit--) {
  93                if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
  94                        break;
  95                mdelay(10);
  96        }
  97        if (limit < 0)
  98                return -EBUSY;
  99
 100        return 0;
 101}
 102
 103static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
 104                int add_sub, int gmac4)
 105{
 106        u32 value;
 107        int limit;
 108
 109        if (add_sub) {
 110                /* If the new sec value needs to be subtracted with
 111                 * the system time, then MAC_STSUR reg should be
 112                 * programmed with (2^32 – <new_sec_value>)
 113                 */
 114                if (gmac4)
 115                        sec = -sec;
 116
 117                value = readl(ioaddr + PTP_TCR);
 118                if (value & PTP_TCR_TSCTRLSSR)
 119                        nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec);
 120                else
 121                        nsec = (PTP_BINARY_ROLLOVER_MODE - nsec);
 122        }
 123
 124        writel(sec, ioaddr + PTP_STSUR);
 125        value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec;
 126        writel(value, ioaddr + PTP_STNSUR);
 127
 128        /* issue command to initialize the system time value */
 129        value = readl(ioaddr + PTP_TCR);
 130        value |= PTP_TCR_TSUPDT;
 131        writel(value, ioaddr + PTP_TCR);
 132
 133        /* wait for present system time adjust/update to complete */
 134        limit = 10;
 135        while (limit--) {
 136                if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT))
 137                        break;
 138                mdelay(10);
 139        }
 140        if (limit < 0)
 141                return -EBUSY;
 142
 143        return 0;
 144}
 145
 146static void get_systime(void __iomem *ioaddr, u64 *systime)
 147{
 148        u64 ns;
 149
 150        /* Get the TSSS value */
 151        ns = readl(ioaddr + PTP_STNSR);
 152        /* Get the TSS and convert sec time value to nanosecond */
 153        ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
 154
 155        if (systime)
 156                *systime = ns;
 157}
 158
 159static void get_ptptime(void __iomem *ptpaddr, u64 *ptp_time)
 160{
 161        u64 ns;
 162
 163        ns = readl(ptpaddr + PTP_ATNR);
 164        ns += readl(ptpaddr + PTP_ATSR) * NSEC_PER_SEC;
 165
 166        *ptp_time = ns;
 167}
 168
 169static void timestamp_interrupt(struct stmmac_priv *priv)
 170{
 171        u32 num_snapshot, ts_status, tsync_int;
 172        struct ptp_clock_event event;
 173        unsigned long flags;
 174        u64 ptp_time;
 175        int i;
 176
 177        tsync_int = readl(priv->ioaddr + GMAC_INT_STATUS) & GMAC_INT_TSIE;
 178
 179        if (!tsync_int)
 180                return;
 181
 182        /* Read timestamp status to clear interrupt from either external
 183         * timestamp or start/end of PPS.
 184         */
 185        ts_status = readl(priv->ioaddr + GMAC_TIMESTAMP_STATUS);
 186
 187        if (!priv->plat->ext_snapshot_en)
 188                return;
 189
 190        num_snapshot = (ts_status & GMAC_TIMESTAMP_ATSNS_MASK) >>
 191                       GMAC_TIMESTAMP_ATSNS_SHIFT;
 192
 193        for (i = 0; i < num_snapshot; i++) {
 194                spin_lock_irqsave(&priv->ptp_lock, flags);
 195                get_ptptime(priv->ptpaddr, &ptp_time);
 196                spin_unlock_irqrestore(&priv->ptp_lock, flags);
 197                event.type = PTP_CLOCK_EXTTS;
 198                event.index = 0;
 199                event.timestamp = ptp_time;
 200                ptp_clock_event(priv->ptp_clock, &event);
 201        }
 202}
 203
 204const struct stmmac_hwtimestamp stmmac_ptp = {
 205        .config_hw_tstamping = config_hw_tstamping,
 206        .init_systime = init_systime,
 207        .config_sub_second_increment = config_sub_second_increment,
 208        .config_addend = config_addend,
 209        .adjust_systime = adjust_systime,
 210        .get_systime = get_systime,
 211        .get_ptptime = get_ptptime,
 212        .timestamp_interrupt = timestamp_interrupt,
 213};
 214