1 // SPDX-License-Identifier: GPL-2.0-only
2 /*******************************************************************************
3   PTP 1588 clock using the STMMAC.
4 
5   Copyright (C) 2013  Vayavya Labs Pvt Ltd
6 
7 
8   Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
9 *******************************************************************************/
10 #include "stmmac.h"
11 #include "stmmac_ptp.h"
12 #include "dwmac4.h"
13 
14 /**
15  * stmmac_adjust_freq
16  *
17  * @ptp: pointer to ptp_clock_info structure
18  * @ppb: desired period change in parts ber billion
19  *
20  * Description: this function will adjust the frequency of hardware clock.
21  */
22 static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
23 {
24 	struct stmmac_priv *priv =
25 	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
26 	unsigned long flags;
27 	u32 diff, addend;
28 	int neg_adj = 0;
29 	u64 adj;
30 
31 	if (ppb < 0) {
32 		neg_adj = 1;
33 		ppb = -ppb;
34 	}
35 
36 	addend = priv->default_addend;
37 	adj = addend;
38 	adj *= ppb;
39 	diff = div_u64(adj, 1000000000ULL);
40 	addend = neg_adj ? (addend - diff) : (addend + diff);
41 
42 	spin_lock_irqsave(&priv->ptp_lock, flags);
43 	stmmac_config_addend(priv, priv->ptpaddr, addend);
44 	spin_unlock_irqrestore(&priv->ptp_lock, flags);
45 
46 	return 0;
47 }
48 
49 /**
50  * stmmac_adjust_time
51  *
52  * @ptp: pointer to ptp_clock_info structure
53  * @delta: desired change in nanoseconds
54  *
55  * Description: this function will shift/adjust the hardware clock time.
56  */
57 static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
58 {
59 	struct stmmac_priv *priv =
60 	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
61 	unsigned long flags;
62 	u32 sec, nsec;
63 	u32 quotient, reminder;
64 	int neg_adj = 0;
65 	bool xmac;
66 
67 	xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
68 
69 	if (delta < 0) {
70 		neg_adj = 1;
71 		delta = -delta;
72 	}
73 
74 	quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
75 	sec = quotient;
76 	nsec = reminder;
77 
78 	spin_lock_irqsave(&priv->ptp_lock, flags);
79 	stmmac_adjust_systime(priv, priv->ptpaddr, sec, nsec, neg_adj, xmac);
80 	spin_unlock_irqrestore(&priv->ptp_lock, flags);
81 
82 	return 0;
83 }
84 
85 /**
86  * stmmac_get_time
87  *
88  * @ptp: pointer to ptp_clock_info structure
89  * @ts: pointer to hold time/result
90  *
91  * Description: this function will read the current time from the
92  * hardware clock and store it in @ts.
93  */
94 static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
95 {
96 	struct stmmac_priv *priv =
97 	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
98 	unsigned long flags;
99 	u64 ns = 0;
100 
101 	spin_lock_irqsave(&priv->ptp_lock, flags);
102 	stmmac_get_systime(priv, priv->ptpaddr, &ns);
103 	spin_unlock_irqrestore(&priv->ptp_lock, flags);
104 
105 	*ts = ns_to_timespec64(ns);
106 
107 	return 0;
108 }
109 
110 /**
111  * stmmac_set_time
112  *
113  * @ptp: pointer to ptp_clock_info structure
114  * @ts: time value to set
115  *
116  * Description: this function will set the current time on the
117  * hardware clock.
118  */
119 static int stmmac_set_time(struct ptp_clock_info *ptp,
120 			   const struct timespec64 *ts)
121 {
122 	struct stmmac_priv *priv =
123 	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
124 	unsigned long flags;
125 
126 	spin_lock_irqsave(&priv->ptp_lock, flags);
127 	stmmac_init_systime(priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec);
128 	spin_unlock_irqrestore(&priv->ptp_lock, flags);
129 
130 	return 0;
131 }
132 
133 static int stmmac_enable(struct ptp_clock_info *ptp,
134 			 struct ptp_clock_request *rq, int on)
135 {
136 	struct stmmac_priv *priv =
137 	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
138 	void __iomem *ptpaddr = priv->ptpaddr;
139 	void __iomem *ioaddr = priv->hw->pcsr;
140 	struct stmmac_pps_cfg *cfg;
141 	u32 intr_value, acr_value;
142 	int ret = -EOPNOTSUPP;
143 	unsigned long flags;
144 
145 	switch (rq->type) {
146 	case PTP_CLK_REQ_PEROUT:
147 		/* Reject requests with unsupported flags */
148 		if (rq->perout.flags)
149 			return -EOPNOTSUPP;
150 
151 		cfg = &priv->pps[rq->perout.index];
152 
153 		cfg->start.tv_sec = rq->perout.start.sec;
154 		cfg->start.tv_nsec = rq->perout.start.nsec;
155 		cfg->period.tv_sec = rq->perout.period.sec;
156 		cfg->period.tv_nsec = rq->perout.period.nsec;
157 
158 		spin_lock_irqsave(&priv->ptp_lock, flags);
159 		ret = stmmac_flex_pps_config(priv, priv->ioaddr,
160 					     rq->perout.index, cfg, on,
161 					     priv->sub_second_inc,
162 					     priv->systime_flags);
163 		spin_unlock_irqrestore(&priv->ptp_lock, flags);
164 		break;
165 	case PTP_CLK_REQ_EXTTS:
166 		priv->plat->ext_snapshot_en = on;
167 		mutex_lock(&priv->aux_ts_lock);
168 		acr_value = readl(ptpaddr + PTP_ACR);
169 		acr_value &= ~PTP_ACR_MASK;
170 		if (on) {
171 			/* Enable External snapshot trigger */
172 			acr_value |= priv->plat->ext_snapshot_num;
173 			acr_value |= PTP_ACR_ATSFC;
174 			netdev_dbg(priv->dev, "Auxiliary Snapshot %d enabled.\n",
175 				   priv->plat->ext_snapshot_num >>
176 				   PTP_ACR_ATSEN_SHIFT);
177 			/* Enable Timestamp Interrupt */
178 			intr_value = readl(ioaddr + GMAC_INT_EN);
179 			intr_value |= GMAC_INT_TSIE;
180 			writel(intr_value, ioaddr + GMAC_INT_EN);
181 
182 		} else {
183 			netdev_dbg(priv->dev, "Auxiliary Snapshot %d disabled.\n",
184 				   priv->plat->ext_snapshot_num >>
185 				   PTP_ACR_ATSEN_SHIFT);
186 			/* Disable Timestamp Interrupt */
187 			intr_value = readl(ioaddr + GMAC_INT_EN);
188 			intr_value &= ~GMAC_INT_TSIE;
189 			writel(intr_value, ioaddr + GMAC_INT_EN);
190 		}
191 		writel(acr_value, ptpaddr + PTP_ACR);
192 		mutex_unlock(&priv->aux_ts_lock);
193 		ret = 0;
194 		break;
195 
196 	default:
197 		break;
198 	}
199 
200 	return ret;
201 }
202 
203 /**
204  * stmmac_get_syncdevicetime
205  * @device: current device time
206  * @system: system counter value read synchronously with device time
207  * @ctx: context provided by timekeeping code
208  * Description: Read device and system clock simultaneously and return the
209  * corrected clock values in ns.
210  **/
211 static int stmmac_get_syncdevicetime(ktime_t *device,
212 				     struct system_counterval_t *system,
213 				     void *ctx)
214 {
215 	struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
216 
217 	if (priv->plat->crosststamp)
218 		return priv->plat->crosststamp(device, system, ctx);
219 	else
220 		return -EOPNOTSUPP;
221 }
222 
223 static int stmmac_getcrosststamp(struct ptp_clock_info *ptp,
224 				 struct system_device_crosststamp *xtstamp)
225 {
226 	struct stmmac_priv *priv =
227 		container_of(ptp, struct stmmac_priv, ptp_clock_ops);
228 
229 	return get_device_system_crosststamp(stmmac_get_syncdevicetime,
230 					     priv, NULL, xtstamp);
231 }
232 
233 /* structure describing a PTP hardware clock */
234 static struct ptp_clock_info stmmac_ptp_clock_ops = {
235 	.owner = THIS_MODULE,
236 	.name = "stmmac ptp",
237 	.max_adj = 62500000,
238 	.n_alarm = 0,
239 	.n_ext_ts = 0, /* will be overwritten in stmmac_ptp_register */
240 	.n_per_out = 0, /* will be overwritten in stmmac_ptp_register */
241 	.n_pins = 0,
242 	.pps = 0,
243 	.adjfreq = stmmac_adjust_freq,
244 	.adjtime = stmmac_adjust_time,
245 	.gettime64 = stmmac_get_time,
246 	.settime64 = stmmac_set_time,
247 	.enable = stmmac_enable,
248 	.getcrosststamp = stmmac_getcrosststamp,
249 };
250 
251 /**
252  * stmmac_ptp_register
253  * @priv: driver private structure
254  * Description: this function will register the ptp clock driver
255  * to kernel. It also does some house keeping work.
256  */
257 void stmmac_ptp_register(struct stmmac_priv *priv)
258 {
259 	int i;
260 
261 	if (priv->plat->ptp_clk_freq_config)
262 		priv->plat->ptp_clk_freq_config(priv);
263 
264 	for (i = 0; i < priv->dma_cap.pps_out_num; i++) {
265 		if (i >= STMMAC_PPS_MAX)
266 			break;
267 		priv->pps[i].available = true;
268 	}
269 
270 	if (priv->plat->ptp_max_adj)
271 		stmmac_ptp_clock_ops.max_adj = priv->plat->ptp_max_adj;
272 
273 	stmmac_ptp_clock_ops.n_per_out = priv->dma_cap.pps_out_num;
274 	stmmac_ptp_clock_ops.n_ext_ts = priv->dma_cap.aux_snapshot_n;
275 
276 	spin_lock_init(&priv->ptp_lock);
277 	mutex_init(&priv->aux_ts_lock);
278 	priv->ptp_clock_ops = stmmac_ptp_clock_ops;
279 
280 	priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
281 					     priv->device);
282 	if (IS_ERR(priv->ptp_clock)) {
283 		netdev_err(priv->dev, "ptp_clock_register failed\n");
284 		priv->ptp_clock = NULL;
285 	} else if (priv->ptp_clock)
286 		netdev_info(priv->dev, "registered PTP clock\n");
287 }
288 
289 /**
290  * stmmac_ptp_unregister
291  * @priv: driver private structure
292  * Description: this function will remove/unregister the ptp clock driver
293  * from the kernel.
294  */
295 void stmmac_ptp_unregister(struct stmmac_priv *priv)
296 {
297 	if (priv->ptp_clock) {
298 		ptp_clock_unregister(priv->ptp_clock);
299 		priv->ptp_clock = NULL;
300 		pr_debug("Removed PTP HW clock successfully on %s\n",
301 			 priv->dev->name);
302 	}
303 
304 	mutex_destroy(&priv->aux_ts_lock);
305 }
306