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 "common.h" 16 #include "stmmac_ptp.h" 17 18 static void config_hw_tstamping(void __iomem *ioaddr, u32 data) 19 { 20 writel(data, ioaddr + PTP_TCR); 21 } 22 23 static void config_sub_second_increment(void __iomem *ioaddr, 24 u32 ptp_clock, int gmac4, u32 *ssinc) 25 { 26 u32 value = readl(ioaddr + PTP_TCR); 27 unsigned long data; 28 u32 reg_value; 29 30 /* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second 31 * formula = (1/ptp_clock) * 1000000000 32 * where ptp_clock is 50MHz if fine method is used to update system 33 */ 34 if (value & PTP_TCR_TSCFUPDT) 35 data = (1000000000ULL / 50000000); 36 else 37 data = (1000000000ULL / ptp_clock); 38 39 /* 0.465ns accuracy */ 40 if (!(value & PTP_TCR_TSCTRLSSR)) 41 data = (data * 1000) / 465; 42 43 data &= PTP_SSIR_SSINC_MASK; 44 45 reg_value = data; 46 if (gmac4) 47 reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT; 48 49 writel(reg_value, ioaddr + PTP_SSIR); 50 51 if (ssinc) 52 *ssinc = data; 53 } 54 55 static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) 56 { 57 u32 value; 58 59 writel(sec, ioaddr + PTP_STSUR); 60 writel(nsec, ioaddr + PTP_STNSUR); 61 /* issue command to initialize the system time value */ 62 value = readl(ioaddr + PTP_TCR); 63 value |= PTP_TCR_TSINIT; 64 writel(value, ioaddr + PTP_TCR); 65 66 /* wait for present system time initialize to complete */ 67 return readl_poll_timeout(ioaddr + PTP_TCR, value, 68 !(value & PTP_TCR_TSINIT), 69 10000, 100000); 70 } 71 72 static int config_addend(void __iomem *ioaddr, u32 addend) 73 { 74 u32 value; 75 int limit; 76 77 writel(addend, ioaddr + PTP_TAR); 78 /* issue command to update the addend value */ 79 value = readl(ioaddr + PTP_TCR); 80 value |= PTP_TCR_TSADDREG; 81 writel(value, ioaddr + PTP_TCR); 82 83 /* wait for present addend update to complete */ 84 limit = 10; 85 while (limit--) { 86 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG)) 87 break; 88 mdelay(10); 89 } 90 if (limit < 0) 91 return -EBUSY; 92 93 return 0; 94 } 95 96 static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec, 97 int add_sub, int gmac4) 98 { 99 u32 value; 100 int limit; 101 102 if (add_sub) { 103 /* If the new sec value needs to be subtracted with 104 * the system time, then MAC_STSUR reg should be 105 * programmed with (2^32 – <new_sec_value>) 106 */ 107 if (gmac4) 108 sec = -sec; 109 110 value = readl(ioaddr + PTP_TCR); 111 if (value & PTP_TCR_TSCTRLSSR) 112 nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec); 113 else 114 nsec = (PTP_BINARY_ROLLOVER_MODE - nsec); 115 } 116 117 writel(sec, ioaddr + PTP_STSUR); 118 value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec; 119 writel(value, ioaddr + PTP_STNSUR); 120 121 /* issue command to initialize the system time value */ 122 value = readl(ioaddr + PTP_TCR); 123 value |= PTP_TCR_TSUPDT; 124 writel(value, ioaddr + PTP_TCR); 125 126 /* wait for present system time adjust/update to complete */ 127 limit = 10; 128 while (limit--) { 129 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT)) 130 break; 131 mdelay(10); 132 } 133 if (limit < 0) 134 return -EBUSY; 135 136 return 0; 137 } 138 139 static void get_systime(void __iomem *ioaddr, u64 *systime) 140 { 141 u64 ns; 142 143 /* Get the TSSS value */ 144 ns = readl(ioaddr + PTP_STNSR); 145 /* Get the TSS and convert sec time value to nanosecond */ 146 ns += readl(ioaddr + PTP_STSR) * 1000000000ULL; 147 148 if (systime) 149 *systime = ns; 150 } 151 152 const struct stmmac_hwtimestamp stmmac_ptp = { 153 .config_hw_tstamping = config_hw_tstamping, 154 .init_systime = init_systime, 155 .config_sub_second_increment = config_sub_second_increment, 156 .config_addend = config_addend, 157 .adjust_systime = adjust_systime, 158 .get_systime = get_systime, 159 }; 160