GNU Linux-libre 4.9.318-gnu1
[releases.git] / drivers / net / ethernet / stmicro / stmmac / stmmac_hwtstamp.c
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         u32 reg_value;
42
43         /* For GMAC3.x, 4.x versions, in "fine adjustement mode" set sub-second
44          * increment to twice the number of nanoseconds of a clock cycle.
45          * The calculation of the default_addend value by the caller will set it
46          * to mid-range = 2^31 when the remainder of this division is zero,
47          * which will make the accumulator overflow once every 2 ptp_clock
48          * cycles, adding twice the number of nanoseconds of a clock cycle :
49          * 2000000000ULL / ptp_clock.
50          */
51         if (value & PTP_TCR_TSCFUPDT)
52                 data = (2000000000ULL / ptp_clock);
53         else
54                 data = (1000000000ULL / ptp_clock);
55
56         /* 0.465ns accuracy */
57         if (!(value & PTP_TCR_TSCTRLSSR))
58                 data = (data * 1000) / 465;
59
60         data &= PTP_SSIR_SSINC_MASK;
61
62         reg_value = data;
63         if (gmac4)
64                 reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
65
66         writel(reg_value, ioaddr + PTP_SSIR);
67
68         return data;
69 }
70
71 static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
72 {
73         int limit;
74         u32 value;
75
76         writel(sec, ioaddr + PTP_STSUR);
77         writel(nsec, ioaddr + PTP_STNSUR);
78         /* issue command to initialize the system time value */
79         value = readl(ioaddr + PTP_TCR);
80         value |= PTP_TCR_TSINIT;
81         writel(value, ioaddr + PTP_TCR);
82
83         /* wait for present system time initialize to complete */
84         limit = 10;
85         while (limit--) {
86                 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
87                         break;
88                 mdelay(10);
89         }
90         if (limit < 0)
91                 return -EBUSY;
92
93         return 0;
94 }
95
96 static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
97 {
98         u32 value;
99         int limit;
100
101         writel(addend, ioaddr + PTP_TAR);
102         /* issue command to update the addend value */
103         value = readl(ioaddr + PTP_TCR);
104         value |= PTP_TCR_TSADDREG;
105         writel(value, ioaddr + PTP_TCR);
106
107         /* wait for present addend update to complete */
108         limit = 10;
109         while (limit--) {
110                 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
111                         break;
112                 mdelay(10);
113         }
114         if (limit < 0)
115                 return -EBUSY;
116
117         return 0;
118 }
119
120 static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
121                                  int add_sub, int gmac4)
122 {
123         u32 value;
124         int limit;
125
126         if (add_sub) {
127                 /* If the new sec value needs to be subtracted with
128                  * the system time, then MAC_STSUR reg should be
129                  * programmed with (2^32 – <new_sec_value>)
130                  */
131                 if (gmac4)
132                         sec = -sec;
133
134                 value = readl(ioaddr + PTP_TCR);
135                 if (value & PTP_TCR_TSCTRLSSR)
136                         nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec);
137                 else
138                         nsec = (PTP_BINARY_ROLLOVER_MODE - nsec);
139         }
140
141         writel(sec, ioaddr + PTP_STSUR);
142         value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec;
143         writel(value, ioaddr + PTP_STNSUR);
144
145         /* issue command to initialize the system time value */
146         value = readl(ioaddr + PTP_TCR);
147         value |= PTP_TCR_TSUPDT;
148         writel(value, ioaddr + PTP_TCR);
149
150         /* wait for present system time adjust/update to complete */
151         limit = 10;
152         while (limit--) {
153                 if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT))
154                         break;
155                 mdelay(10);
156         }
157         if (limit < 0)
158                 return -EBUSY;
159
160         return 0;
161 }
162
163 static u64 stmmac_get_systime(void __iomem *ioaddr)
164 {
165         u64 ns;
166
167         /* Get the TSSS value */
168         ns = readl(ioaddr + PTP_STNSR);
169         /* Get the TSS and convert sec time value to nanosecond */
170         ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
171
172         return ns;
173 }
174
175 const struct stmmac_hwtimestamp stmmac_ptp = {
176         .config_hw_tstamping = stmmac_config_hw_tstamping,
177         .init_systime = stmmac_init_systime,
178         .config_sub_second_increment = stmmac_config_sub_second_increment,
179         .config_addend = stmmac_config_addend,
180         .adjust_systime = stmmac_adjust_systime,
181         .get_systime = stmmac_get_systime,
182 };