]>
Commit | Line | Data |
---|---|---|
4fa9c49f | 1 | // SPDX-License-Identifier: GPL-2.0-only |
891434b1 RK |
2 | /******************************************************************************* |
3 | Copyright (C) 2013 Vayavya Labs Pvt Ltd | |
4 | ||
5 | This implements all the API for managing HW timestamp & PTP. | |
6 | ||
891434b1 RK |
7 | |
8 | Author: Rayagond Kokatanur <rayagond@vayavyalabs.com> | |
9 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | |
10 | *******************************************************************************/ | |
11 | ||
12 | #include <linux/io.h> | |
ff8ed737 | 13 | #include <linux/iopoll.h> |
891434b1 | 14 | #include <linux/delay.h> |
f4da5652 | 15 | #include <linux/ptp_clock_kernel.h> |
891434b1 RK |
16 | #include "common.h" |
17 | #include "stmmac_ptp.h" | |
f4da5652 TTM |
18 | #include "dwmac4.h" |
19 | #include "stmmac.h" | |
891434b1 | 20 | |
cc4c9001 | 21 | static void config_hw_tstamping(void __iomem *ioaddr, u32 data) |
891434b1 RK |
22 | { |
23 | writel(data, ioaddr + PTP_TCR); | |
24 | } | |
25 | ||
cc4c9001 JA |
26 | static void config_sub_second_increment(void __iomem *ioaddr, |
27 | u32 ptp_clock, int gmac4, u32 *ssinc) | |
891434b1 RK |
28 | { |
29 | u32 value = readl(ioaddr + PTP_TCR); | |
30 | unsigned long data; | |
200922c9 | 31 | u32 reg_value; |
891434b1 | 32 | |
91a2559c JB |
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. | |
891434b1 | 40 | */ |
ba1ffd74 | 41 | if (value & PTP_TCR_TSCFUPDT) |
91a2559c | 42 | data = (2000000000ULL / ptp_clock); |
ba1ffd74 GC |
43 | else |
44 | data = (1000000000ULL / ptp_clock); | |
891434b1 RK |
45 | |
46 | /* 0.465ns accuracy */ | |
0cf91580 SZ |
47 | if (!(value & PTP_TCR_TSCTRLSSR)) |
48 | data = (data * 1000) / 465; | |
891434b1 | 49 | |
ba1ffd74 GC |
50 | data &= PTP_SSIR_SSINC_MASK; |
51 | ||
200922c9 | 52 | reg_value = data; |
ba1ffd74 | 53 | if (gmac4) |
200922c9 | 54 | reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT; |
ba1ffd74 | 55 | |
200922c9 | 56 | writel(reg_value, ioaddr + PTP_SSIR); |
19d857c9 | 57 | |
cc4c9001 JA |
58 | if (ssinc) |
59 | *ssinc = data; | |
891434b1 RK |
60 | } |
61 | ||
cc4c9001 | 62 | static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) |
891434b1 | 63 | { |
891434b1 RK |
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 */ | |
ff8ed737 DZ |
74 | return readl_poll_timeout(ioaddr + PTP_TCR, value, |
75 | !(value & PTP_TCR_TSINIT), | |
76 | 10000, 100000); | |
891434b1 RK |
77 | } |
78 | ||
cc4c9001 | 79 | static int config_addend(void __iomem *ioaddr, u32 addend) |
891434b1 RK |
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 | ||
cc4c9001 JA |
103 | static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec, |
104 | int add_sub, int gmac4) | |
92ba6888 RK |
105 | { |
106 | u32 value; | |
107 | int limit; | |
108 | ||
ba1ffd74 GC |
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) | |
a1e5388b | 115 | sec = -sec; |
ba1ffd74 GC |
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 | ||
92ba6888 | 124 | writel(sec, ioaddr + PTP_STSUR); |
ba1ffd74 GC |
125 | value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec; |
126 | writel(value, ioaddr + PTP_STNSUR); | |
127 | ||
92ba6888 RK |
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 | ||
cc4c9001 | 146 | static void get_systime(void __iomem *ioaddr, u64 *systime) |
92ba6888 | 147 | { |
000b6d37 YV |
148 | u64 ns, sec0, sec1; |
149 | ||
150 | /* Get the TSS value */ | |
151 | sec1 = readl_relaxed(ioaddr + PTP_STSR); | |
152 | do { | |
153 | sec0 = sec1; | |
154 | /* Get the TSSS value */ | |
155 | ns = readl_relaxed(ioaddr + PTP_STNSR); | |
156 | /* Get the TSS value */ | |
157 | sec1 = readl_relaxed(ioaddr + PTP_STSR); | |
158 | } while (sec0 != sec1); | |
92ba6888 | 159 | |
cc4c9001 | 160 | if (systime) |
000b6d37 | 161 | *systime = ns + (sec1 * 1000000000ULL); |
92ba6888 RK |
162 | } |
163 | ||
341f67e4 TTM |
164 | static void get_ptptime(void __iomem *ptpaddr, u64 *ptp_time) |
165 | { | |
166 | u64 ns; | |
167 | ||
168 | ns = readl(ptpaddr + PTP_ATNR); | |
169 | ns += readl(ptpaddr + PTP_ATSR) * NSEC_PER_SEC; | |
170 | ||
171 | *ptp_time = ns; | |
172 | } | |
173 | ||
f4da5652 TTM |
174 | static void timestamp_interrupt(struct stmmac_priv *priv) |
175 | { | |
176 | u32 num_snapshot, ts_status, tsync_int; | |
177 | struct ptp_clock_event event; | |
178 | unsigned long flags; | |
179 | u64 ptp_time; | |
180 | int i; | |
181 | ||
182 | tsync_int = readl(priv->ioaddr + GMAC_INT_STATUS) & GMAC_INT_TSIE; | |
183 | ||
184 | if (!tsync_int) | |
185 | return; | |
186 | ||
187 | /* Read timestamp status to clear interrupt from either external | |
188 | * timestamp or start/end of PPS. | |
189 | */ | |
190 | ts_status = readl(priv->ioaddr + GMAC_TIMESTAMP_STATUS); | |
191 | ||
192 | if (!priv->plat->ext_snapshot_en) | |
193 | return; | |
194 | ||
195 | num_snapshot = (ts_status & GMAC_TIMESTAMP_ATSNS_MASK) >> | |
196 | GMAC_TIMESTAMP_ATSNS_SHIFT; | |
197 | ||
198 | for (i = 0; i < num_snapshot; i++) { | |
199 | spin_lock_irqsave(&priv->ptp_lock, flags); | |
200 | get_ptptime(priv->ptpaddr, &ptp_time); | |
201 | spin_unlock_irqrestore(&priv->ptp_lock, flags); | |
202 | event.type = PTP_CLOCK_EXTTS; | |
203 | event.index = 0; | |
204 | event.timestamp = ptp_time; | |
205 | ptp_clock_event(priv->ptp_clock, &event); | |
206 | } | |
207 | } | |
208 | ||
891434b1 | 209 | const struct stmmac_hwtimestamp stmmac_ptp = { |
cc4c9001 JA |
210 | .config_hw_tstamping = config_hw_tstamping, |
211 | .init_systime = init_systime, | |
212 | .config_sub_second_increment = config_sub_second_increment, | |
213 | .config_addend = config_addend, | |
214 | .adjust_systime = adjust_systime, | |
215 | .get_systime = get_systime, | |
341f67e4 | 216 | .get_ptptime = get_ptptime, |
f4da5652 | 217 | .timestamp_interrupt = timestamp_interrupt, |
891434b1 | 218 | }; |