1 /******************************************************************************* 2 Copyright (C) 2013 Vayavya Labs Pvt Ltd 3 4 This implements all the API for managing HW timestamp & PTP. 5 6 This program is free software; you can redistribute it and/or modify it 7 under the terms and conditions of the GNU General Public License, 8 version 2, as published by the Free Software Foundation. 9 10 This program is distributed in the hope it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 more details. 14 15 You should have received a copy of the GNU General Public License along with 16 this program; if not, write to the Free Software Foundation, Inc., 17 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 18 19 The full GNU General Public License is included in this distribution in 20 the file called "COPYING". 21 22 Author: Rayagond Kokatanur <rayagond@vayavyalabs.com> 23 Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 24 *******************************************************************************/ 25 26 #include <linux/io.h> 27 #include <linux/delay.h> 28 #include "common.h" 29 #include "stmmac_ptp.h" 30 31 static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data) 32 { 33 writel(data, ioaddr + PTP_TCR); 34 } 35 36 static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr, 37 u32 ptp_clock, int gmac4) 38 { 39 u32 value = readl(ioaddr + PTP_TCR); 40 unsigned long data; 41 42 /* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second 43 * formula = (1/ptp_clock) * 1000000000 44 * where ptp_clock is 50MHz if fine method is used to update system 45 */ 46 if (value & PTP_TCR_TSCFUPDT) 47 data = (1000000000ULL / 50000000); 48 else 49 data = (1000000000ULL / ptp_clock); 50 51 /* 0.465ns accuracy */ 52 if (!(value & PTP_TCR_TSCTRLSSR)) 53 data = (data * 1000) / 465; 54 55 data &= PTP_SSIR_SSINC_MASK; 56 57 if (gmac4) 58 data = data << GMAC4_PTP_SSIR_SSINC_SHIFT; 59 60 writel(data, ioaddr + PTP_SSIR); 61 62 return data; 63 } 64 65 static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) 66 { 67 int limit; 68 u32 value; 69 70 writel(sec, ioaddr + PTP_STSUR); 71 writel(nsec, ioaddr + PTP_STNSUR); 72 /* issue command to initialize the system time value */ 73 value = readl(ioaddr + PTP_TCR); 74 value |= PTP_TCR_TSINIT; 75 writel(value, ioaddr + PTP_TCR); 76 77 /* wait for present system time initialize to complete */ 78 limit = 10; 79 while (limit--) { 80 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT)) 81 break; 82 mdelay(10); 83 } 84 if (limit < 0) 85 return -EBUSY; 86 87 return 0; 88 } 89 90 static int stmmac_config_addend(void __iomem *ioaddr, u32 addend) 91 { 92 u32 value; 93 int limit; 94 95 writel(addend, ioaddr + PTP_TAR); 96 /* issue command to update the addend value */ 97 value = readl(ioaddr + PTP_TCR); 98 value |= PTP_TCR_TSADDREG; 99 writel(value, ioaddr + PTP_TCR); 100 101 /* wait for present addend update to complete */ 102 limit = 10; 103 while (limit--) { 104 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG)) 105 break; 106 mdelay(10); 107 } 108 if (limit < 0) 109 return -EBUSY; 110 111 return 0; 112 } 113 114 static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec, 115 int add_sub, int gmac4) 116 { 117 u32 value; 118 int limit; 119 120 if (add_sub) { 121 /* If the new sec value needs to be subtracted with 122 * the system time, then MAC_STSUR reg should be 123 * programmed with (2^32 – <new_sec_value>) 124 */ 125 if (gmac4) 126 sec = (100000000ULL - sec); 127 128 value = readl(ioaddr + PTP_TCR); 129 if (value & PTP_TCR_TSCTRLSSR) 130 nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec); 131 else 132 nsec = (PTP_BINARY_ROLLOVER_MODE - nsec); 133 } 134 135 writel(sec, ioaddr + PTP_STSUR); 136 value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec; 137 writel(value, ioaddr + PTP_STNSUR); 138 139 /* issue command to initialize the system time value */ 140 value = readl(ioaddr + PTP_TCR); 141 value |= PTP_TCR_TSUPDT; 142 writel(value, ioaddr + PTP_TCR); 143 144 /* wait for present system time adjust/update to complete */ 145 limit = 10; 146 while (limit--) { 147 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT)) 148 break; 149 mdelay(10); 150 } 151 if (limit < 0) 152 return -EBUSY; 153 154 return 0; 155 } 156 157 static u64 stmmac_get_systime(void __iomem *ioaddr) 158 { 159 u64 ns; 160 161 /* Get the TSSS value */ 162 ns = readl(ioaddr + PTP_STNSR); 163 /* Get the TSS and convert sec time value to nanosecond */ 164 ns += readl(ioaddr + PTP_STSR) * 1000000000ULL; 165 166 return ns; 167 } 168 169 const struct stmmac_hwtimestamp stmmac_ptp = { 170 .config_hw_tstamping = stmmac_config_hw_tstamping, 171 .init_systime = stmmac_init_systime, 172 .config_sub_second_increment = stmmac_config_sub_second_increment, 173 .config_addend = stmmac_config_addend, 174 .adjust_systime = stmmac_adjust_systime, 175 .get_systime = stmmac_get_systime, 176 }; 177