]>
Commit | Line | Data |
---|---|---|
bb77f36a VO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com> | |
3 | */ | |
34d76e9f | 4 | #include <linux/spi/spi.h> |
bb77f36a VO |
5 | #include "sja1105.h" |
6 | ||
7 | /* The adjfine API clamps ppb between [-32,768,000, 32,768,000], and | |
8 | * therefore scaled_ppm between [-2,147,483,648, 2,147,483,647]. | |
9 | * Set the maximum supported ppb to a round value smaller than the maximum. | |
10 | * | |
11 | * Percentually speaking, this is a +/- 0.032x adjustment of the | |
12 | * free-running counter (0.968x to 1.032x). | |
13 | */ | |
14 | #define SJA1105_MAX_ADJ_PPB 32000000 | |
15 | #define SJA1105_SIZE_PTP_CMD 4 | |
16 | ||
747e5eb3 VO |
17 | /* PTPSYNCTS has no interrupt or update mechanism, because the intended |
18 | * hardware use case is for the timestamp to be collected synchronously, | |
0ba83aa0 VO |
19 | * immediately after the CAS_MASTER SJA1105 switch has performed a CASSYNC |
20 | * one-shot toggle (no return to level) on the PTP_CLK pin. When used as a | |
21 | * generic extts source, the PTPSYNCTS register needs polling and a comparison | |
22 | * with the old value. The polling interval is configured as the Nyquist rate | |
23 | * of a signal with 50% duty cycle and 1Hz frequency, which is sadly all that | |
24 | * this hardware can do (but may be enough for some setups). Anything of higher | |
25 | * frequency than 1 Hz will be lost, since there is no timestamp FIFO. | |
747e5eb3 | 26 | */ |
0ba83aa0 | 27 | #define SJA1105_EXTTS_INTERVAL (HZ / 4) |
747e5eb3 | 28 | |
bb77f36a VO |
29 | /* This range is actually +/- SJA1105_MAX_ADJ_PPB |
30 | * divided by 1000 (ppb -> ppm) and with a 16-bit | |
31 | * "fractional" part (actually fixed point). | |
32 | * | | |
33 | * v | |
34 | * Convert scaled_ppm from the +/- ((10^6) << 16) range | |
35 | * into the +/- (1 << 31) range. | |
36 | * | |
37 | * This forgoes a "ppb" numeric representation (up to NSEC_PER_SEC) | |
38 | * and defines the scaling factor between scaled_ppm and the actual | |
2fb079a2 | 39 | * frequency adjustments of the PHC. |
bb77f36a VO |
40 | * |
41 | * ptpclkrate = scaled_ppm * 2^31 / (10^6 * 2^16) | |
42 | * simplifies to | |
43 | * ptpclkrate = scaled_ppm * 2^9 / 5^6 | |
44 | */ | |
45 | #define SJA1105_CC_MULT_NUM (1 << 9) | |
46 | #define SJA1105_CC_MULT_DEM 15625 | |
2fb079a2 VO |
47 | #define SJA1105_CC_MULT 0x80000000 |
48 | ||
49 | enum sja1105_ptp_clk_mode { | |
50 | PTP_ADD_MODE = 1, | |
51 | PTP_SET_MODE = 0, | |
52 | }; | |
bb77f36a | 53 | |
747e5eb3 VO |
54 | #define extts_to_data(d) \ |
55 | container_of((d), struct sja1105_ptp_data, extts_work) | |
a9d6ed7a VO |
56 | #define ptp_caps_to_data(d) \ |
57 | container_of((d), struct sja1105_ptp_data, caps) | |
a9d6ed7a VO |
58 | #define ptp_data_to_sja1105(d) \ |
59 | container_of((d), struct sja1105_private, ptp_data) | |
bb77f36a | 60 | |
a9d6ed7a VO |
61 | /* Must be called only with priv->tagger_data.state bit |
62 | * SJA1105_HWTS_RX_EN cleared | |
63 | */ | |
64 | static int sja1105_change_rxtstamping(struct sja1105_private *priv, | |
65 | bool on) | |
66 | { | |
19d1f0ed | 67 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; |
a9d6ed7a VO |
68 | struct sja1105_general_params_entry *general_params; |
69 | struct sja1105_table *table; | |
a9d6ed7a VO |
70 | |
71 | table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; | |
72 | general_params = table->entries; | |
73 | general_params->send_meta1 = on; | |
74 | general_params->send_meta0 = on; | |
75 | ||
a9d6ed7a VO |
76 | /* Initialize the meta state machine to a known state */ |
77 | if (priv->tagger_data.stampable_skb) { | |
78 | kfree_skb(priv->tagger_data.stampable_skb); | |
79 | priv->tagger_data.stampable_skb = NULL; | |
80 | } | |
19d1f0ed VO |
81 | ptp_cancel_worker_sync(ptp_data->clock); |
82 | skb_queue_purge(&ptp_data->skb_rxtstamp_queue); | |
a9d6ed7a | 83 | |
2eea1fa8 | 84 | return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); |
a9d6ed7a VO |
85 | } |
86 | ||
87 | int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) | |
88 | { | |
89 | struct sja1105_private *priv = ds->priv; | |
90 | struct hwtstamp_config config; | |
91 | bool rx_on; | |
92 | int rc; | |
93 | ||
94 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) | |
95 | return -EFAULT; | |
96 | ||
97 | switch (config.tx_type) { | |
98 | case HWTSTAMP_TX_OFF: | |
99 | priv->ports[port].hwts_tx_en = false; | |
100 | break; | |
101 | case HWTSTAMP_TX_ON: | |
102 | priv->ports[port].hwts_tx_en = true; | |
103 | break; | |
104 | default: | |
105 | return -ERANGE; | |
106 | } | |
107 | ||
108 | switch (config.rx_filter) { | |
109 | case HWTSTAMP_FILTER_NONE: | |
110 | rx_on = false; | |
111 | break; | |
112 | default: | |
113 | rx_on = true; | |
114 | break; | |
115 | } | |
116 | ||
117 | if (rx_on != test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) { | |
118 | clear_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); | |
119 | ||
120 | rc = sja1105_change_rxtstamping(priv, rx_on); | |
121 | if (rc < 0) { | |
122 | dev_err(ds->dev, | |
123 | "Failed to change RX timestamping: %d\n", rc); | |
124 | return rc; | |
125 | } | |
126 | if (rx_on) | |
127 | set_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); | |
128 | } | |
129 | ||
130 | if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) | |
131 | return -EFAULT; | |
132 | return 0; | |
133 | } | |
134 | ||
135 | int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) | |
136 | { | |
137 | struct sja1105_private *priv = ds->priv; | |
138 | struct hwtstamp_config config; | |
139 | ||
140 | config.flags = 0; | |
141 | if (priv->ports[port].hwts_tx_en) | |
142 | config.tx_type = HWTSTAMP_TX_ON; | |
143 | else | |
144 | config.tx_type = HWTSTAMP_TX_OFF; | |
145 | if (test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) | |
146 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; | |
147 | else | |
148 | config.rx_filter = HWTSTAMP_FILTER_NONE; | |
149 | ||
150 | return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? | |
151 | -EFAULT : 0; | |
152 | } | |
153 | ||
bb77f36a VO |
154 | int sja1105_get_ts_info(struct dsa_switch *ds, int port, |
155 | struct ethtool_ts_info *info) | |
156 | { | |
157 | struct sja1105_private *priv = ds->priv; | |
a9d6ed7a | 158 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; |
bb77f36a VO |
159 | |
160 | /* Called during cleanup */ | |
a9d6ed7a | 161 | if (!ptp_data->clock) |
bb77f36a VO |
162 | return -ENODEV; |
163 | ||
164 | info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | | |
165 | SOF_TIMESTAMPING_RX_HARDWARE | | |
166 | SOF_TIMESTAMPING_RAW_HARDWARE; | |
a602afd2 VO |
167 | info->tx_types = (1 << HWTSTAMP_TX_OFF) | |
168 | (1 << HWTSTAMP_TX_ON); | |
169 | info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | | |
170 | (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT); | |
a9d6ed7a | 171 | info->phc_index = ptp_clock_index(ptp_data->clock); |
bb77f36a VO |
172 | return 0; |
173 | } | |
bb77f36a | 174 | |
41603d78 VO |
175 | void sja1105et_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd, |
176 | enum packing_op op) | |
bb77f36a | 177 | { |
bb77f36a | 178 | const int size = SJA1105_SIZE_PTP_CMD; |
bb77f36a VO |
179 | /* No need to keep this as part of the structure */ |
180 | u64 valid = 1; | |
181 | ||
41603d78 | 182 | sja1105_packing(buf, &valid, 31, 31, size, op); |
86db36a3 VO |
183 | sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op); |
184 | sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op); | |
747e5eb3 VO |
185 | sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op); |
186 | sja1105_packing(buf, &cmd->stopptpcp, 27, 27, size, op); | |
41603d78 VO |
187 | sja1105_packing(buf, &cmd->resptp, 2, 2, size, op); |
188 | sja1105_packing(buf, &cmd->corrclk4ts, 1, 1, size, op); | |
189 | sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op); | |
bb77f36a | 190 | } |
bb77f36a | 191 | |
41603d78 VO |
192 | void sja1105pqrs_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd, |
193 | enum packing_op op) | |
bb77f36a | 194 | { |
bb77f36a | 195 | const int size = SJA1105_SIZE_PTP_CMD; |
bb77f36a VO |
196 | /* No need to keep this as part of the structure */ |
197 | u64 valid = 1; | |
198 | ||
41603d78 | 199 | sja1105_packing(buf, &valid, 31, 31, size, op); |
86db36a3 VO |
200 | sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op); |
201 | sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op); | |
747e5eb3 VO |
202 | sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op); |
203 | sja1105_packing(buf, &cmd->stopptpcp, 27, 27, size, op); | |
41603d78 VO |
204 | sja1105_packing(buf, &cmd->resptp, 3, 3, size, op); |
205 | sja1105_packing(buf, &cmd->corrclk4ts, 2, 2, size, op); | |
206 | sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op); | |
207 | } | |
208 | ||
86db36a3 VO |
209 | int sja1105_ptp_commit(struct dsa_switch *ds, struct sja1105_ptp_cmd *cmd, |
210 | sja1105_spi_rw_mode_t rw) | |
41603d78 | 211 | { |
86db36a3 | 212 | const struct sja1105_private *priv = ds->priv; |
41603d78 VO |
213 | const struct sja1105_regs *regs = priv->info->regs; |
214 | u8 buf[SJA1105_SIZE_PTP_CMD] = {0}; | |
215 | int rc; | |
216 | ||
217 | if (rw == SPI_WRITE) | |
218 | priv->info->ptp_cmd_packing(buf, cmd, PACK); | |
bb77f36a | 219 | |
5a47f588 | 220 | rc = sja1105_xfer_buf(priv, rw, regs->ptp_control, buf, |
41603d78 VO |
221 | SJA1105_SIZE_PTP_CMD); |
222 | ||
223 | if (rw == SPI_READ) | |
224 | priv->info->ptp_cmd_packing(buf, cmd, UNPACK); | |
225 | ||
226 | return rc; | |
bb77f36a | 227 | } |
bb77f36a | 228 | |
47ed985e VO |
229 | /* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap |
230 | * around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35 | |
231 | * seconds). | |
232 | * | |
233 | * This receives the RX or TX MAC timestamps, provided by hardware as | |
234 | * the lower bits of the cycle counter, sampled at the time the timestamp was | |
235 | * collected. | |
236 | * | |
237 | * To reconstruct into a full 64-bit-wide timestamp, the cycle counter is | |
238 | * read and the high-order bits are filled in. | |
239 | * | |
240 | * Must be called within one wraparound period of the partial timestamp since | |
241 | * it was generated by the MAC. | |
242 | */ | |
a9d6ed7a VO |
243 | static u64 sja1105_tstamp_reconstruct(struct dsa_switch *ds, u64 now, |
244 | u64 ts_partial) | |
47ed985e | 245 | { |
61c77126 | 246 | struct sja1105_private *priv = ds->priv; |
47ed985e VO |
247 | u64 partial_tstamp_mask = CYCLECOUNTER_MASK(priv->info->ptp_ts_bits); |
248 | u64 ts_reconstructed; | |
249 | ||
250 | ts_reconstructed = (now & ~partial_tstamp_mask) | ts_partial; | |
251 | ||
252 | /* Check lower bits of current cycle counter against the timestamp. | |
253 | * If the current cycle counter is lower than the partial timestamp, | |
254 | * then wraparound surely occurred and must be accounted for. | |
255 | */ | |
256 | if ((now & partial_tstamp_mask) <= ts_partial) | |
257 | ts_reconstructed -= (partial_tstamp_mask + 1); | |
258 | ||
259 | return ts_reconstructed; | |
260 | } | |
47ed985e VO |
261 | |
262 | /* Reads the SPI interface for an egress timestamp generated by the switch | |
263 | * for frames sent using management routes. | |
264 | * | |
265 | * SJA1105 E/T layout of the 4-byte SPI payload: | |
266 | * | |
267 | * 31 23 15 7 0 | |
268 | * | | | | | | |
269 | * +-----+-----+-----+ ^ | |
270 | * ^ | | |
271 | * | | | |
272 | * 24-bit timestamp Update bit | |
273 | * | |
274 | * | |
275 | * SJA1105 P/Q/R/S layout of the 8-byte SPI payload: | |
276 | * | |
277 | * 31 23 15 7 0 63 55 47 39 32 | |
278 | * | | | | | | | | | | | |
279 | * ^ +-----+-----+-----+-----+ | |
280 | * | ^ | |
281 | * | | | |
282 | * Update bit 32-bit timestamp | |
283 | * | |
284 | * Notice that the update bit is in the same place. | |
285 | * To have common code for E/T and P/Q/R/S for reading the timestamp, | |
286 | * we need to juggle with the offset and the bit indices. | |
287 | */ | |
a9d6ed7a | 288 | static int sja1105_ptpegr_ts_poll(struct dsa_switch *ds, int port, u64 *ts) |
47ed985e | 289 | { |
61c77126 | 290 | struct sja1105_private *priv = ds->priv; |
47ed985e VO |
291 | const struct sja1105_regs *regs = priv->info->regs; |
292 | int tstamp_bit_start, tstamp_bit_end; | |
293 | int timeout = 10; | |
294 | u8 packed_buf[8]; | |
295 | u64 update; | |
296 | int rc; | |
297 | ||
298 | do { | |
1bd44870 VO |
299 | rc = sja1105_xfer_buf(priv, SPI_READ, regs->ptpegr_ts[port], |
300 | packed_buf, priv->info->ptpegr_ts_bytes); | |
47ed985e VO |
301 | if (rc < 0) |
302 | return rc; | |
303 | ||
304 | sja1105_unpack(packed_buf, &update, 0, 0, | |
305 | priv->info->ptpegr_ts_bytes); | |
306 | if (update) | |
307 | break; | |
308 | ||
309 | usleep_range(10, 50); | |
310 | } while (--timeout); | |
311 | ||
312 | if (!timeout) | |
313 | return -ETIMEDOUT; | |
314 | ||
315 | /* Point the end bit to the second 32-bit word on P/Q/R/S, | |
316 | * no-op on E/T. | |
317 | */ | |
318 | tstamp_bit_end = (priv->info->ptpegr_ts_bytes - 4) * 8; | |
319 | /* Shift the 24-bit timestamp on E/T to be collected from 31:8. | |
320 | * No-op on P/Q/R/S. | |
321 | */ | |
322 | tstamp_bit_end += 32 - priv->info->ptp_ts_bits; | |
323 | tstamp_bit_start = tstamp_bit_end + priv->info->ptp_ts_bits - 1; | |
324 | ||
325 | *ts = 0; | |
326 | ||
327 | sja1105_unpack(packed_buf, ts, tstamp_bit_start, tstamp_bit_end, | |
328 | priv->info->ptpegr_ts_bytes); | |
329 | ||
330 | return 0; | |
331 | } | |
47ed985e | 332 | |
2fb079a2 | 333 | /* Caller must hold ptp_data->lock */ |
34d76e9f VO |
334 | static int sja1105_ptpclkval_read(struct sja1105_private *priv, u64 *ticks, |
335 | struct ptp_system_timestamp *ptp_sts) | |
2fb079a2 VO |
336 | { |
337 | const struct sja1105_regs *regs = priv->info->regs; | |
338 | ||
34d76e9f VO |
339 | return sja1105_xfer_u64(priv, SPI_READ, regs->ptpclkval, ticks, |
340 | ptp_sts); | |
2fb079a2 VO |
341 | } |
342 | ||
343 | /* Caller must hold ptp_data->lock */ | |
6cf99c13 VO |
344 | static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks, |
345 | struct ptp_system_timestamp *ptp_sts) | |
2fb079a2 VO |
346 | { |
347 | const struct sja1105_regs *regs = priv->info->regs; | |
348 | ||
34d76e9f | 349 | return sja1105_xfer_u64(priv, SPI_WRITE, regs->ptpclkval, &ticks, |
6cf99c13 | 350 | ptp_sts); |
2fb079a2 VO |
351 | } |
352 | ||
1e762bd2 | 353 | static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) |
a9d6ed7a | 354 | { |
1e762bd2 VO |
355 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); |
356 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); | |
a9d6ed7a VO |
357 | struct dsa_switch *ds = priv->ds; |
358 | struct sk_buff *skb; | |
359 | ||
360 | mutex_lock(&ptp_data->lock); | |
361 | ||
1e762bd2 | 362 | while ((skb = skb_dequeue(&ptp_data->skb_rxtstamp_queue)) != NULL) { |
a9d6ed7a | 363 | struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); |
2fb079a2 VO |
364 | u64 ticks, ts; |
365 | int rc; | |
a9d6ed7a | 366 | |
34d76e9f | 367 | rc = sja1105_ptpclkval_read(priv, &ticks, NULL); |
2fb079a2 VO |
368 | if (rc < 0) { |
369 | dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc); | |
370 | kfree_skb(skb); | |
371 | continue; | |
372 | } | |
a9d6ed7a VO |
373 | |
374 | *shwt = (struct skb_shared_hwtstamps) {0}; | |
375 | ||
376 | ts = SJA1105_SKB_CB(skb)->meta_tstamp; | |
2fb079a2 | 377 | ts = sja1105_tstamp_reconstruct(ds, ticks, ts); |
a9d6ed7a | 378 | |
2fb079a2 | 379 | shwt->hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts)); |
a9d6ed7a VO |
380 | netif_rx_ni(skb); |
381 | } | |
382 | ||
383 | mutex_unlock(&ptp_data->lock); | |
1e762bd2 VO |
384 | |
385 | /* Don't restart */ | |
386 | return -1; | |
a9d6ed7a VO |
387 | } |
388 | ||
389 | /* Called from dsa_skb_defer_rx_timestamp */ | |
390 | bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, | |
391 | struct sk_buff *skb, unsigned int type) | |
392 | { | |
393 | struct sja1105_private *priv = ds->priv; | |
1e762bd2 | 394 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; |
a9d6ed7a | 395 | |
1e762bd2 | 396 | if (!test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) |
a9d6ed7a VO |
397 | return false; |
398 | ||
399 | /* We need to read the full PTP clock to reconstruct the Rx | |
400 | * timestamp. For that we need a sleepable context. | |
401 | */ | |
1e762bd2 VO |
402 | skb_queue_tail(&ptp_data->skb_rxtstamp_queue, skb); |
403 | ptp_schedule_worker(ptp_data->clock, 0); | |
a9d6ed7a VO |
404 | return true; |
405 | } | |
406 | ||
407 | /* Called from dsa_skb_tx_timestamp. This callback is just to make DSA clone | |
408 | * the skb and have it available in DSA_SKB_CB in the .port_deferred_xmit | |
409 | * callback, where we will timestamp it synchronously. | |
410 | */ | |
411 | bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, | |
412 | struct sk_buff *skb, unsigned int type) | |
413 | { | |
414 | struct sja1105_private *priv = ds->priv; | |
415 | struct sja1105_port *sp = &priv->ports[port]; | |
416 | ||
417 | if (!sp->hwts_tx_en) | |
418 | return false; | |
419 | ||
420 | return true; | |
421 | } | |
422 | ||
6cf99c13 | 423 | static int sja1105_ptp_reset(struct dsa_switch *ds) |
bb77f36a | 424 | { |
61c77126 | 425 | struct sja1105_private *priv = ds->priv; |
a9d6ed7a | 426 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; |
66427778 | 427 | struct sja1105_ptp_cmd cmd = ptp_data->cmd; |
bb77f36a VO |
428 | int rc; |
429 | ||
a9d6ed7a | 430 | mutex_lock(&ptp_data->lock); |
bb77f36a VO |
431 | |
432 | cmd.resptp = 1; | |
41603d78 | 433 | |
bb77f36a | 434 | dev_dbg(ds->dev, "Resetting PTP clock\n"); |
86db36a3 VO |
435 | rc = sja1105_ptp_commit(ds, &cmd, SPI_WRITE); |
436 | ||
437 | sja1105_tas_clockstep(priv->ds); | |
bb77f36a | 438 | |
a9d6ed7a | 439 | mutex_unlock(&ptp_data->lock); |
bb77f36a VO |
440 | |
441 | return rc; | |
442 | } | |
bb77f36a | 443 | |
6cf99c13 VO |
444 | /* Caller must hold ptp_data->lock */ |
445 | int __sja1105_ptp_gettimex(struct dsa_switch *ds, u64 *ns, | |
446 | struct ptp_system_timestamp *ptp_sts) | |
447 | { | |
448 | struct sja1105_private *priv = ds->priv; | |
449 | u64 ticks; | |
450 | int rc; | |
451 | ||
452 | rc = sja1105_ptpclkval_read(priv, &ticks, ptp_sts); | |
453 | if (rc < 0) { | |
454 | dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc); | |
455 | return rc; | |
456 | } | |
457 | ||
458 | *ns = sja1105_ticks_to_ns(ticks); | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
34d76e9f VO |
463 | static int sja1105_ptp_gettimex(struct ptp_clock_info *ptp, |
464 | struct timespec64 *ts, | |
465 | struct ptp_system_timestamp *ptp_sts) | |
bb77f36a | 466 | { |
a9d6ed7a | 467 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); |
2fb079a2 | 468 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); |
6cf99c13 | 469 | u64 now = 0; |
2fb079a2 | 470 | int rc; |
bb77f36a | 471 | |
a9d6ed7a | 472 | mutex_lock(&ptp_data->lock); |
2fb079a2 | 473 | |
6cf99c13 VO |
474 | rc = __sja1105_ptp_gettimex(priv->ds, &now, ptp_sts); |
475 | *ts = ns_to_timespec64(now); | |
2fb079a2 | 476 | |
a9d6ed7a | 477 | mutex_unlock(&ptp_data->lock); |
bb77f36a | 478 | |
2fb079a2 VO |
479 | return rc; |
480 | } | |
bb77f36a | 481 | |
2fb079a2 VO |
482 | /* Caller must hold ptp_data->lock */ |
483 | static int sja1105_ptp_mode_set(struct sja1105_private *priv, | |
484 | enum sja1105_ptp_clk_mode mode) | |
485 | { | |
486 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; | |
487 | ||
488 | if (ptp_data->cmd.ptpclkadd == mode) | |
489 | return 0; | |
490 | ||
491 | ptp_data->cmd.ptpclkadd = mode; | |
492 | ||
86db36a3 | 493 | return sja1105_ptp_commit(priv->ds, &ptp_data->cmd, SPI_WRITE); |
bb77f36a VO |
494 | } |
495 | ||
2fb079a2 | 496 | /* Write to PTPCLKVAL while PTPCLKADD is 0 */ |
6cf99c13 VO |
497 | int __sja1105_ptp_settime(struct dsa_switch *ds, u64 ns, |
498 | struct ptp_system_timestamp *ptp_sts) | |
499 | { | |
500 | struct sja1105_private *priv = ds->priv; | |
501 | u64 ticks = ns_to_sja1105_ticks(ns); | |
502 | int rc; | |
503 | ||
504 | rc = sja1105_ptp_mode_set(priv, PTP_SET_MODE); | |
505 | if (rc < 0) { | |
506 | dev_err(priv->ds->dev, "Failed to put PTPCLK in set mode\n"); | |
507 | return rc; | |
508 | } | |
509 | ||
86db36a3 VO |
510 | rc = sja1105_ptpclkval_write(priv, ticks, ptp_sts); |
511 | ||
512 | sja1105_tas_clockstep(priv->ds); | |
513 | ||
514 | return rc; | |
6cf99c13 VO |
515 | } |
516 | ||
bb77f36a VO |
517 | static int sja1105_ptp_settime(struct ptp_clock_info *ptp, |
518 | const struct timespec64 *ts) | |
519 | { | |
a9d6ed7a | 520 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); |
2fb079a2 | 521 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); |
6cf99c13 | 522 | u64 ns = timespec64_to_ns(ts); |
2fb079a2 | 523 | int rc; |
bb77f36a | 524 | |
a9d6ed7a | 525 | mutex_lock(&ptp_data->lock); |
2fb079a2 | 526 | |
6cf99c13 | 527 | rc = __sja1105_ptp_settime(priv->ds, ns, NULL); |
2fb079a2 | 528 | |
a9d6ed7a | 529 | mutex_unlock(&ptp_data->lock); |
bb77f36a | 530 | |
2fb079a2 | 531 | return rc; |
bb77f36a VO |
532 | } |
533 | ||
534 | static int sja1105_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) | |
535 | { | |
a9d6ed7a | 536 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); |
2fb079a2 VO |
537 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); |
538 | const struct sja1105_regs *regs = priv->info->regs; | |
539 | u32 clkrate32; | |
bb77f36a | 540 | s64 clkrate; |
2fb079a2 | 541 | int rc; |
bb77f36a VO |
542 | |
543 | clkrate = (s64)scaled_ppm * SJA1105_CC_MULT_NUM; | |
544 | clkrate = div_s64(clkrate, SJA1105_CC_MULT_DEM); | |
545 | ||
2fb079a2 VO |
546 | /* Take a +/- value and re-center it around 2^31. */ |
547 | clkrate = SJA1105_CC_MULT + clkrate; | |
548 | WARN_ON(abs(clkrate) >= GENMASK_ULL(31, 0)); | |
549 | clkrate32 = clkrate; | |
bb77f36a | 550 | |
2fb079a2 | 551 | mutex_lock(&ptp_data->lock); |
bb77f36a | 552 | |
34d76e9f VO |
553 | rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptpclkrate, &clkrate32, |
554 | NULL); | |
bb77f36a | 555 | |
86db36a3 VO |
556 | sja1105_tas_adjfreq(priv->ds); |
557 | ||
a9d6ed7a | 558 | mutex_unlock(&ptp_data->lock); |
bb77f36a | 559 | |
2fb079a2 | 560 | return rc; |
bb77f36a VO |
561 | } |
562 | ||
2fb079a2 | 563 | /* Write to PTPCLKVAL while PTPCLKADD is 1 */ |
6cf99c13 | 564 | int __sja1105_ptp_adjtime(struct dsa_switch *ds, s64 delta) |
bb77f36a | 565 | { |
6cf99c13 | 566 | struct sja1105_private *priv = ds->priv; |
2fb079a2 | 567 | s64 ticks = ns_to_sja1105_ticks(delta); |
bb77f36a VO |
568 | int rc; |
569 | ||
2fb079a2 VO |
570 | rc = sja1105_ptp_mode_set(priv, PTP_ADD_MODE); |
571 | if (rc < 0) { | |
572 | dev_err(priv->ds->dev, "Failed to put PTPCLK in add mode\n"); | |
6cf99c13 | 573 | return rc; |
2fb079a2 | 574 | } |
bb77f36a | 575 | |
86db36a3 VO |
576 | rc = sja1105_ptpclkval_write(priv, ticks, NULL); |
577 | ||
578 | sja1105_tas_clockstep(priv->ds); | |
579 | ||
580 | return rc; | |
6cf99c13 VO |
581 | } |
582 | ||
583 | static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | |
584 | { | |
585 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); | |
586 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); | |
587 | int rc; | |
588 | ||
589 | mutex_lock(&ptp_data->lock); | |
590 | ||
591 | rc = __sja1105_ptp_adjtime(priv->ds, delta); | |
bb77f36a | 592 | |
2fb079a2 VO |
593 | mutex_unlock(&ptp_data->lock); |
594 | ||
595 | return rc; | |
bb77f36a VO |
596 | } |
597 | ||
747e5eb3 VO |
598 | static void sja1105_ptp_extts_work(struct work_struct *work) |
599 | { | |
600 | struct delayed_work *dw = to_delayed_work(work); | |
601 | struct sja1105_ptp_data *ptp_data = extts_to_data(dw); | |
602 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); | |
603 | const struct sja1105_regs *regs = priv->info->regs; | |
604 | struct ptp_clock_event event; | |
605 | u64 ptpsyncts = 0; | |
606 | int rc; | |
607 | ||
608 | mutex_lock(&ptp_data->lock); | |
609 | ||
610 | rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts, | |
611 | NULL); | |
612 | if (rc < 0) | |
613 | dev_err_ratelimited(priv->ds->dev, | |
614 | "Failed to read PTPSYNCTS: %d\n", rc); | |
615 | ||
616 | if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) { | |
617 | event.index = 0; | |
618 | event.type = PTP_CLOCK_EXTTS; | |
619 | event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts)); | |
620 | ptp_clock_event(ptp_data->clock, &event); | |
621 | ||
622 | ptp_data->ptpsyncts = ptpsyncts; | |
623 | } | |
624 | ||
625 | mutex_unlock(&ptp_data->lock); | |
626 | ||
627 | schedule_delayed_work(&ptp_data->extts_work, SJA1105_EXTTS_INTERVAL); | |
628 | } | |
629 | ||
630 | static int sja1105_change_ptp_clk_pin_func(struct sja1105_private *priv, | |
631 | enum ptp_pin_function func) | |
632 | { | |
633 | struct sja1105_avb_params_entry *avb; | |
634 | enum ptp_pin_function old_func; | |
635 | ||
636 | avb = priv->static_config.tables[BLK_IDX_AVB_PARAMS].entries; | |
637 | ||
638 | if (priv->info->device_id == SJA1105E_DEVICE_ID || | |
639 | priv->info->device_id == SJA1105T_DEVICE_ID || | |
640 | avb->cas_master) | |
641 | old_func = PTP_PF_PEROUT; | |
642 | else | |
643 | old_func = PTP_PF_EXTTS; | |
644 | ||
645 | if (func == old_func) | |
646 | return 0; | |
647 | ||
648 | avb->cas_master = (func == PTP_PF_PEROUT); | |
649 | ||
650 | return sja1105_dynamic_config_write(priv, BLK_IDX_AVB_PARAMS, 0, avb, | |
651 | true); | |
652 | } | |
653 | ||
654 | /* The PTP_CLK pin may be configured to toggle with a 50% duty cycle and a | |
655 | * frequency f: | |
656 | * | |
657 | * NSEC_PER_SEC | |
658 | * f = ---------------------- | |
659 | * (PTPPINDUR * 8 ns) * 2 | |
660 | */ | |
661 | static int sja1105_per_out_enable(struct sja1105_private *priv, | |
662 | struct ptp_perout_request *perout, | |
663 | bool on) | |
664 | { | |
665 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; | |
666 | const struct sja1105_regs *regs = priv->info->regs; | |
667 | struct sja1105_ptp_cmd cmd = ptp_data->cmd; | |
668 | int rc; | |
669 | ||
670 | /* We only support one channel */ | |
671 | if (perout->index != 0) | |
672 | return -EOPNOTSUPP; | |
673 | ||
674 | /* Reject requests with unsupported flags */ | |
675 | if (perout->flags) | |
676 | return -EOPNOTSUPP; | |
677 | ||
678 | mutex_lock(&ptp_data->lock); | |
679 | ||
680 | rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_PEROUT); | |
681 | if (rc) | |
682 | goto out; | |
683 | ||
684 | if (on) { | |
685 | struct timespec64 pin_duration_ts = { | |
686 | .tv_sec = perout->period.sec, | |
687 | .tv_nsec = perout->period.nsec, | |
688 | }; | |
689 | struct timespec64 pin_start_ts = { | |
690 | .tv_sec = perout->start.sec, | |
691 | .tv_nsec = perout->start.nsec, | |
692 | }; | |
693 | u64 pin_duration = timespec64_to_ns(&pin_duration_ts); | |
694 | u64 pin_start = timespec64_to_ns(&pin_start_ts); | |
695 | u32 pin_duration32; | |
696 | u64 now; | |
697 | ||
698 | /* ptppindur: 32 bit register which holds the interval between | |
699 | * 2 edges on PTP_CLK. So check for truncation which happens | |
700 | * at periods larger than around 68.7 seconds. | |
701 | */ | |
702 | pin_duration = ns_to_sja1105_ticks(pin_duration / 2); | |
703 | if (pin_duration > U32_MAX) { | |
704 | rc = -ERANGE; | |
705 | goto out; | |
706 | } | |
707 | pin_duration32 = pin_duration; | |
708 | ||
709 | /* ptppins: 64 bit register which needs to hold a PTP time | |
710 | * larger than the current time, otherwise the startptpcp | |
711 | * command won't do anything. So advance the current time | |
712 | * by a number of periods in a way that won't alter the | |
713 | * phase offset. | |
714 | */ | |
715 | rc = __sja1105_ptp_gettimex(priv->ds, &now, NULL); | |
716 | if (rc < 0) | |
717 | goto out; | |
718 | ||
719 | pin_start = future_base_time(pin_start, pin_duration, | |
720 | now + 1ull * NSEC_PER_SEC); | |
721 | pin_start = ns_to_sja1105_ticks(pin_start); | |
722 | ||
723 | rc = sja1105_xfer_u64(priv, SPI_WRITE, regs->ptppinst, | |
724 | &pin_start, NULL); | |
725 | if (rc < 0) | |
726 | goto out; | |
727 | ||
728 | rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptppindur, | |
729 | &pin_duration32, NULL); | |
730 | if (rc < 0) | |
731 | goto out; | |
732 | } | |
733 | ||
734 | if (on) | |
735 | cmd.startptpcp = true; | |
736 | else | |
737 | cmd.stopptpcp = true; | |
738 | ||
739 | rc = sja1105_ptp_commit(priv->ds, &cmd, SPI_WRITE); | |
740 | ||
741 | out: | |
742 | mutex_unlock(&ptp_data->lock); | |
743 | ||
744 | return rc; | |
745 | } | |
746 | ||
747 | static int sja1105_extts_enable(struct sja1105_private *priv, | |
748 | struct ptp_extts_request *extts, | |
749 | bool on) | |
750 | { | |
751 | int rc; | |
752 | ||
753 | /* We only support one channel */ | |
754 | if (extts->index != 0) | |
755 | return -EOPNOTSUPP; | |
756 | ||
757 | /* Reject requests with unsupported flags */ | |
0ba83aa0 VO |
758 | if (extts->flags & ~(PTP_ENABLE_FEATURE | |
759 | PTP_RISING_EDGE | | |
760 | PTP_FALLING_EDGE | | |
761 | PTP_STRICT_FLAGS)) | |
762 | return -EOPNOTSUPP; | |
763 | ||
764 | /* We can only enable time stamping on both edges, sadly. */ | |
765 | if ((extts->flags & PTP_STRICT_FLAGS) && | |
766 | (extts->flags & PTP_ENABLE_FEATURE) && | |
767 | (extts->flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES) | |
747e5eb3 VO |
768 | return -EOPNOTSUPP; |
769 | ||
770 | rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_EXTTS); | |
771 | if (rc) | |
772 | return rc; | |
773 | ||
774 | if (on) | |
775 | schedule_delayed_work(&priv->ptp_data.extts_work, | |
776 | SJA1105_EXTTS_INTERVAL); | |
777 | else | |
778 | cancel_delayed_work_sync(&priv->ptp_data.extts_work); | |
779 | ||
780 | return 0; | |
781 | } | |
782 | ||
783 | static int sja1105_ptp_enable(struct ptp_clock_info *ptp, | |
784 | struct ptp_clock_request *req, int on) | |
785 | { | |
786 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); | |
787 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); | |
788 | int rc = -EOPNOTSUPP; | |
789 | ||
790 | if (req->type == PTP_CLK_REQ_PEROUT) | |
791 | rc = sja1105_per_out_enable(priv, &req->perout, on); | |
792 | else if (req->type == PTP_CLK_REQ_EXTTS) | |
793 | rc = sja1105_extts_enable(priv, &req->extts, on); | |
794 | ||
795 | return rc; | |
796 | } | |
797 | ||
798 | static int sja1105_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, | |
799 | enum ptp_pin_function func, unsigned int chan) | |
800 | { | |
801 | struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); | |
802 | struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); | |
803 | ||
804 | if (chan != 0 || pin != 0) | |
805 | return -1; | |
806 | ||
807 | switch (func) { | |
808 | case PTP_PF_NONE: | |
809 | case PTP_PF_PEROUT: | |
810 | break; | |
811 | case PTP_PF_EXTTS: | |
812 | if (priv->info->device_id == SJA1105E_DEVICE_ID || | |
813 | priv->info->device_id == SJA1105T_DEVICE_ID) | |
814 | return -1; | |
815 | break; | |
816 | default: | |
817 | return -1; | |
818 | } | |
819 | return 0; | |
820 | } | |
821 | ||
822 | static struct ptp_pin_desc sja1105_ptp_pin = { | |
823 | .name = "ptp_clk", | |
824 | .index = 0, | |
825 | .func = PTP_PF_NONE, | |
826 | }; | |
827 | ||
61c77126 | 828 | int sja1105_ptp_clock_register(struct dsa_switch *ds) |
bb77f36a | 829 | { |
61c77126 | 830 | struct sja1105_private *priv = ds->priv; |
a9d6ed7a VO |
831 | struct sja1105_tagger_data *tagger_data = &priv->tagger_data; |
832 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; | |
bb77f36a | 833 | |
a9d6ed7a | 834 | ptp_data->caps = (struct ptp_clock_info) { |
5b3ae43a VO |
835 | .owner = THIS_MODULE, |
836 | .name = "SJA1105 PHC", | |
837 | .adjfine = sja1105_ptp_adjfine, | |
838 | .adjtime = sja1105_ptp_adjtime, | |
34d76e9f | 839 | .gettimex64 = sja1105_ptp_gettimex, |
5b3ae43a | 840 | .settime64 = sja1105_ptp_settime, |
747e5eb3 VO |
841 | .enable = sja1105_ptp_enable, |
842 | .verify = sja1105_ptp_verify_pin, | |
1e762bd2 | 843 | .do_aux_work = sja1105_rxtstamp_work, |
5b3ae43a | 844 | .max_adj = SJA1105_MAX_ADJ_PPB, |
747e5eb3 VO |
845 | .pin_config = &sja1105_ptp_pin, |
846 | .n_pins = 1, | |
847 | .n_ext_ts = 1, | |
848 | .n_per_out = 1, | |
5b3ae43a VO |
849 | }; |
850 | ||
1e762bd2 | 851 | skb_queue_head_init(&ptp_data->skb_rxtstamp_queue); |
a9d6ed7a | 852 | spin_lock_init(&tagger_data->meta_lock); |
bb77f36a | 853 | |
a9d6ed7a VO |
854 | ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); |
855 | if (IS_ERR_OR_NULL(ptp_data->clock)) | |
856 | return PTR_ERR(ptp_data->clock); | |
bb77f36a | 857 | |
2fb079a2 VO |
858 | ptp_data->cmd.corrclk4ts = true; |
859 | ptp_data->cmd.ptpclkadd = PTP_SET_MODE; | |
6cb0abbd | 860 | |
747e5eb3 VO |
861 | INIT_DELAYED_WORK(&ptp_data->extts_work, sja1105_ptp_extts_work); |
862 | ||
61c77126 | 863 | return sja1105_ptp_reset(ds); |
bb77f36a | 864 | } |
bb77f36a | 865 | |
61c77126 | 866 | void sja1105_ptp_clock_unregister(struct dsa_switch *ds) |
bb77f36a | 867 | { |
61c77126 | 868 | struct sja1105_private *priv = ds->priv; |
a9d6ed7a | 869 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; |
61c77126 | 870 | |
a9d6ed7a | 871 | if (IS_ERR_OR_NULL(ptp_data->clock)) |
bb77f36a VO |
872 | return; |
873 | ||
747e5eb3 | 874 | cancel_delayed_work_sync(&ptp_data->extts_work); |
1e762bd2 VO |
875 | ptp_cancel_worker_sync(ptp_data->clock); |
876 | skb_queue_purge(&ptp_data->skb_rxtstamp_queue); | |
a9d6ed7a VO |
877 | ptp_clock_unregister(ptp_data->clock); |
878 | ptp_data->clock = NULL; | |
879 | } | |
880 | ||
9fcf024d | 881 | void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int port, |
a9d6ed7a VO |
882 | struct sk_buff *skb) |
883 | { | |
884 | struct sja1105_private *priv = ds->priv; | |
885 | struct sja1105_ptp_data *ptp_data = &priv->ptp_data; | |
886 | struct skb_shared_hwtstamps shwt = {0}; | |
2fb079a2 | 887 | u64 ticks, ts; |
a9d6ed7a VO |
888 | int rc; |
889 | ||
890 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; | |
891 | ||
892 | mutex_lock(&ptp_data->lock); | |
893 | ||
34d76e9f | 894 | rc = sja1105_ptpclkval_read(priv, &ticks, NULL); |
2fb079a2 VO |
895 | if (rc < 0) { |
896 | dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc); | |
897 | kfree_skb(skb); | |
898 | goto out; | |
899 | } | |
a9d6ed7a | 900 | |
9fcf024d | 901 | rc = sja1105_ptpegr_ts_poll(ds, port, &ts); |
a9d6ed7a VO |
902 | if (rc < 0) { |
903 | dev_err(ds->dev, "timed out polling for tstamp\n"); | |
904 | kfree_skb(skb); | |
905 | goto out; | |
906 | } | |
907 | ||
2fb079a2 | 908 | ts = sja1105_tstamp_reconstruct(ds, ticks, ts); |
a9d6ed7a | 909 | |
2fb079a2 | 910 | shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts)); |
a9d6ed7a VO |
911 | skb_complete_tx_timestamp(skb, &shwt); |
912 | ||
913 | out: | |
914 | mutex_unlock(&ptp_data->lock); | |
bb77f36a | 915 | } |