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