xref: /openbmc/linux/drivers/net/ethernet/pensando/ionic/ionic_phc.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1fee6efceSShannon Nelson // SPDX-License-Identifier: GPL-2.0
2fee6efceSShannon Nelson /* Copyright(c) 2017 - 2021 Pensando Systems, Inc */
3fee6efceSShannon Nelson 
4fee6efceSShannon Nelson #include <linux/netdevice.h>
5fee6efceSShannon Nelson #include <linux/etherdevice.h>
6fee6efceSShannon Nelson 
7fee6efceSShannon Nelson #include "ionic.h"
8fee6efceSShannon Nelson #include "ionic_bus.h"
9fee6efceSShannon Nelson #include "ionic_lif.h"
10fee6efceSShannon Nelson #include "ionic_ethtool.h"
11fee6efceSShannon Nelson 
ionic_hwstamp_tx_mode(int config_tx_type)12fee6efceSShannon Nelson static int ionic_hwstamp_tx_mode(int config_tx_type)
13fee6efceSShannon Nelson {
14fee6efceSShannon Nelson 	switch (config_tx_type) {
15fee6efceSShannon Nelson 	case HWTSTAMP_TX_OFF:
16fee6efceSShannon Nelson 		return IONIC_TXSTAMP_OFF;
17fee6efceSShannon Nelson 	case HWTSTAMP_TX_ON:
18fee6efceSShannon Nelson 		return IONIC_TXSTAMP_ON;
19fee6efceSShannon Nelson 	case HWTSTAMP_TX_ONESTEP_SYNC:
20fee6efceSShannon Nelson 		return IONIC_TXSTAMP_ONESTEP_SYNC;
21fee6efceSShannon Nelson 	case HWTSTAMP_TX_ONESTEP_P2P:
22fee6efceSShannon Nelson 		return IONIC_TXSTAMP_ONESTEP_P2P;
23fee6efceSShannon Nelson 	default:
24fee6efceSShannon Nelson 		return -ERANGE;
25fee6efceSShannon Nelson 	}
26fee6efceSShannon Nelson }
27fee6efceSShannon Nelson 
ionic_hwstamp_rx_filt(int config_rx_filter)28fee6efceSShannon Nelson static u64 ionic_hwstamp_rx_filt(int config_rx_filter)
29fee6efceSShannon Nelson {
30fee6efceSShannon Nelson 	switch (config_rx_filter) {
31fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
32fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP1_ALL;
33fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
34fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP1_SYNC;
35fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
36fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ;
37fee6efceSShannon Nelson 
38fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
39fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L4_ALL;
40fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
41fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L4_SYNC;
42fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
43fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ;
44fee6efceSShannon Nelson 
45fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
46fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L2_ALL;
47fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
48fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L2_SYNC;
49fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
50fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ;
51fee6efceSShannon Nelson 
52fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
53fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_ALL;
54fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
55fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_SYNC;
56fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
57fee6efceSShannon Nelson 		return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ;
58fee6efceSShannon Nelson 
59fee6efceSShannon Nelson 	case HWTSTAMP_FILTER_NTP_ALL:
60fee6efceSShannon Nelson 		return IONIC_PKT_CLS_NTP_ALL;
61fee6efceSShannon Nelson 
62fee6efceSShannon Nelson 	default:
63fee6efceSShannon Nelson 		return 0;
64fee6efceSShannon Nelson 	}
65fee6efceSShannon Nelson }
66fee6efceSShannon Nelson 
ionic_lif_hwstamp_set_ts_config(struct ionic_lif * lif,struct hwtstamp_config * new_ts)67829600ceSShannon Nelson static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
68829600ceSShannon Nelson 					   struct hwtstamp_config *new_ts)
69fee6efceSShannon Nelson {
70fee6efceSShannon Nelson 	struct ionic *ionic = lif->ionic;
71829600ceSShannon Nelson 	struct hwtstamp_config *config;
72829600ceSShannon Nelson 	struct hwtstamp_config ts;
73fee6efceSShannon Nelson 	int tx_mode = 0;
74fee6efceSShannon Nelson 	u64 rx_filt = 0;
75fee6efceSShannon Nelson 	int err, err2;
76fee6efceSShannon Nelson 	bool rx_all;
77fee6efceSShannon Nelson 	__le64 mask;
78fee6efceSShannon Nelson 
79fee6efceSShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
80fee6efceSShannon Nelson 		return -EOPNOTSUPP;
81fee6efceSShannon Nelson 
82f3318099SShannon Nelson 	mutex_lock(&lif->phc->config_lock);
83f3318099SShannon Nelson 
84829600ceSShannon Nelson 	if (new_ts) {
85829600ceSShannon Nelson 		config = new_ts;
86fee6efceSShannon Nelson 	} else {
87829600ceSShannon Nelson 		/* If called with new_ts == NULL, replay the previous request
88829600ceSShannon Nelson 		 * primarily for recovery after a FW_RESET.
89829600ceSShannon Nelson 		 * We saved the previous configuration request info, so copy
90829600ceSShannon Nelson 		 * the previous request for reference, clear the current state
91829600ceSShannon Nelson 		 * to match the device's reset state, and run with it.
92fee6efceSShannon Nelson 		 */
93829600ceSShannon Nelson 		config = &ts;
94829600ceSShannon Nelson 		memcpy(config, &lif->phc->ts_config, sizeof(*config));
95829600ceSShannon Nelson 		memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config));
96829600ceSShannon Nelson 		lif->phc->ts_config_tx_mode = 0;
97829600ceSShannon Nelson 		lif->phc->ts_config_rx_filt = 0;
98fee6efceSShannon Nelson 	}
99fee6efceSShannon Nelson 
100829600ceSShannon Nelson 	tx_mode = ionic_hwstamp_tx_mode(config->tx_type);
101f3318099SShannon Nelson 	if (tx_mode < 0) {
102f3318099SShannon Nelson 		err = tx_mode;
103f3318099SShannon Nelson 		goto err_queues;
104f3318099SShannon Nelson 	}
105fee6efceSShannon Nelson 
106fee6efceSShannon Nelson 	mask = cpu_to_le64(BIT_ULL(tx_mode));
107f3318099SShannon Nelson 	if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) {
108f3318099SShannon Nelson 		err = -ERANGE;
109f3318099SShannon Nelson 		goto err_queues;
110f3318099SShannon Nelson 	}
111fee6efceSShannon Nelson 
112829600ceSShannon Nelson 	rx_filt = ionic_hwstamp_rx_filt(config->rx_filter);
113829600ceSShannon Nelson 	rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt;
114fee6efceSShannon Nelson 
115fee6efceSShannon Nelson 	mask = cpu_to_le64(rx_filt);
116fee6efceSShannon Nelson 	if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) {
117fee6efceSShannon Nelson 		rx_filt = 0;
118fee6efceSShannon Nelson 		rx_all = true;
119829600ceSShannon Nelson 		config->rx_filter = HWTSTAMP_FILTER_ALL;
120fee6efceSShannon Nelson 	}
121fee6efceSShannon Nelson 
12218d64264SShannon Nelson 	dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n",
12318d64264SShannon Nelson 		__func__, config->rx_filter, rx_filt, rx_all);
124fee6efceSShannon Nelson 
125fee6efceSShannon Nelson 	if (tx_mode) {
126fee6efceSShannon Nelson 		err = ionic_lif_create_hwstamp_txq(lif);
127fee6efceSShannon Nelson 		if (err)
128fee6efceSShannon Nelson 			goto err_queues;
129fee6efceSShannon Nelson 	}
130fee6efceSShannon Nelson 
131fee6efceSShannon Nelson 	if (rx_filt) {
132fee6efceSShannon Nelson 		err = ionic_lif_create_hwstamp_rxq(lif);
133fee6efceSShannon Nelson 		if (err)
134fee6efceSShannon Nelson 			goto err_queues;
135fee6efceSShannon Nelson 	}
136fee6efceSShannon Nelson 
137fee6efceSShannon Nelson 	if (tx_mode != lif->phc->ts_config_tx_mode) {
138fee6efceSShannon Nelson 		err = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
139fee6efceSShannon Nelson 		if (err)
140fee6efceSShannon Nelson 			goto err_txmode;
141fee6efceSShannon Nelson 	}
142fee6efceSShannon Nelson 
143fee6efceSShannon Nelson 	if (rx_filt != lif->phc->ts_config_rx_filt) {
144fee6efceSShannon Nelson 		err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
145fee6efceSShannon Nelson 		if (err)
146fee6efceSShannon Nelson 			goto err_rxfilt;
147fee6efceSShannon Nelson 	}
148fee6efceSShannon Nelson 
149fee6efceSShannon Nelson 	if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) {
150fee6efceSShannon Nelson 		err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all);
151fee6efceSShannon Nelson 		if (err)
152fee6efceSShannon Nelson 			goto err_rxall;
153fee6efceSShannon Nelson 	}
154fee6efceSShannon Nelson 
155829600ceSShannon Nelson 	memcpy(&lif->phc->ts_config, config, sizeof(*config));
156fee6efceSShannon Nelson 	lif->phc->ts_config_rx_filt = rx_filt;
157fee6efceSShannon Nelson 	lif->phc->ts_config_tx_mode = tx_mode;
158fee6efceSShannon Nelson 
159fee6efceSShannon Nelson 	mutex_unlock(&lif->phc->config_lock);
160fee6efceSShannon Nelson 
161fee6efceSShannon Nelson 	return 0;
162fee6efceSShannon Nelson 
163fee6efceSShannon Nelson err_rxall:
164fee6efceSShannon Nelson 	if (rx_filt != lif->phc->ts_config_rx_filt) {
165fee6efceSShannon Nelson 		rx_filt = lif->phc->ts_config_rx_filt;
166fee6efceSShannon Nelson 		err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
167fee6efceSShannon Nelson 		if (err2)
168fee6efceSShannon Nelson 			dev_err(ionic->dev,
169fee6efceSShannon Nelson 				"Failed to revert rx timestamp filter: %d\n", err2);
170fee6efceSShannon Nelson 	}
171fee6efceSShannon Nelson err_rxfilt:
172fee6efceSShannon Nelson 	if (tx_mode != lif->phc->ts_config_tx_mode) {
173fee6efceSShannon Nelson 		tx_mode = lif->phc->ts_config_tx_mode;
174fee6efceSShannon Nelson 		err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
175fee6efceSShannon Nelson 		if (err2)
176fee6efceSShannon Nelson 			dev_err(ionic->dev,
177fee6efceSShannon Nelson 				"Failed to revert tx timestamp mode: %d\n", err2);
178fee6efceSShannon Nelson 	}
179fee6efceSShannon Nelson err_txmode:
180fee6efceSShannon Nelson 	/* special queues remain allocated, just unused */
181fee6efceSShannon Nelson err_queues:
182fee6efceSShannon Nelson 	mutex_unlock(&lif->phc->config_lock);
183fee6efceSShannon Nelson 	return err;
184fee6efceSShannon Nelson }
185fee6efceSShannon Nelson 
ionic_lif_hwstamp_set(struct ionic_lif * lif,struct ifreq * ifr)186829600ceSShannon Nelson int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr)
187829600ceSShannon Nelson {
188829600ceSShannon Nelson 	struct hwtstamp_config config;
189829600ceSShannon Nelson 	int err;
190829600ceSShannon Nelson 
191f79eef71SShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
192f79eef71SShannon Nelson 		return -EOPNOTSUPP;
193f79eef71SShannon Nelson 
194829600ceSShannon Nelson 	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
195829600ceSShannon Nelson 		return -EFAULT;
196829600ceSShannon Nelson 
1977ee99fc5SShannon Nelson 	mutex_lock(&lif->queue_lock);
198829600ceSShannon Nelson 	err = ionic_lif_hwstamp_set_ts_config(lif, &config);
1997ee99fc5SShannon Nelson 	mutex_unlock(&lif->queue_lock);
200829600ceSShannon Nelson 	if (err) {
201829600ceSShannon Nelson 		netdev_info(lif->netdev, "hwstamp set failed: %d\n", err);
202829600ceSShannon Nelson 		return err;
203829600ceSShannon Nelson 	}
204829600ceSShannon Nelson 
205829600ceSShannon Nelson 	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
206829600ceSShannon Nelson 		return -EFAULT;
207829600ceSShannon Nelson 
208829600ceSShannon Nelson 	return 0;
209829600ceSShannon Nelson }
210829600ceSShannon Nelson 
ionic_lif_hwstamp_replay(struct ionic_lif * lif)211f79eef71SShannon Nelson void ionic_lif_hwstamp_replay(struct ionic_lif *lif)
212829600ceSShannon Nelson {
213829600ceSShannon Nelson 	int err;
214829600ceSShannon Nelson 
215f79eef71SShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
216f79eef71SShannon Nelson 		return;
217f79eef71SShannon Nelson 
2187ee99fc5SShannon Nelson 	mutex_lock(&lif->queue_lock);
219829600ceSShannon Nelson 	err = ionic_lif_hwstamp_set_ts_config(lif, NULL);
2207ee99fc5SShannon Nelson 	mutex_unlock(&lif->queue_lock);
221829600ceSShannon Nelson 	if (err)
222829600ceSShannon Nelson 		netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err);
223829600ceSShannon Nelson }
224829600ceSShannon Nelson 
ionic_lif_hwstamp_recreate_queues(struct ionic_lif * lif)225ccbbd002SShannon Nelson void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif)
226ccbbd002SShannon Nelson {
227ccbbd002SShannon Nelson 	int err;
228ccbbd002SShannon Nelson 
229ccbbd002SShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
230ccbbd002SShannon Nelson 		return;
231ccbbd002SShannon Nelson 
232ccbbd002SShannon Nelson 	mutex_lock(&lif->phc->config_lock);
233ccbbd002SShannon Nelson 
234ccbbd002SShannon Nelson 	if (lif->phc->ts_config_tx_mode) {
235ccbbd002SShannon Nelson 		err = ionic_lif_create_hwstamp_txq(lif);
236ccbbd002SShannon Nelson 		if (err)
237ccbbd002SShannon Nelson 			netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err);
238ccbbd002SShannon Nelson 	}
239ccbbd002SShannon Nelson 
240ccbbd002SShannon Nelson 	if (lif->phc->ts_config_rx_filt) {
241ccbbd002SShannon Nelson 		err = ionic_lif_create_hwstamp_rxq(lif);
242ccbbd002SShannon Nelson 		if (err)
243ccbbd002SShannon Nelson 			netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err);
244ccbbd002SShannon Nelson 	}
245ccbbd002SShannon Nelson 
246ccbbd002SShannon Nelson 	mutex_unlock(&lif->phc->config_lock);
247ccbbd002SShannon Nelson }
248ccbbd002SShannon Nelson 
ionic_lif_hwstamp_get(struct ionic_lif * lif,struct ifreq * ifr)249fee6efceSShannon Nelson int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr)
250fee6efceSShannon Nelson {
251fee6efceSShannon Nelson 	struct hwtstamp_config config;
252fee6efceSShannon Nelson 
253fee6efceSShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
254fee6efceSShannon Nelson 		return -EOPNOTSUPP;
255fee6efceSShannon Nelson 
256fee6efceSShannon Nelson 	mutex_lock(&lif->phc->config_lock);
257fee6efceSShannon Nelson 	memcpy(&config, &lif->phc->ts_config, sizeof(config));
258fee6efceSShannon Nelson 	mutex_unlock(&lif->phc->config_lock);
259fee6efceSShannon Nelson 
2605871d0c6SDan Carpenter 	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
2615871d0c6SDan Carpenter 		return -EFAULT;
2625871d0c6SDan Carpenter 	return 0;
263fee6efceSShannon Nelson }
264fee6efceSShannon Nelson 
ionic_hwstamp_read(struct ionic * ionic,struct ptp_system_timestamp * sts)265fee6efceSShannon Nelson static u64 ionic_hwstamp_read(struct ionic *ionic,
266fee6efceSShannon Nelson 			      struct ptp_system_timestamp *sts)
267fee6efceSShannon Nelson {
268fee6efceSShannon Nelson 	u32 tick_high_before, tick_high, tick_low;
269fee6efceSShannon Nelson 
270fee6efceSShannon Nelson 	/* read and discard low part to defeat hw staging of high part */
271896de449SShannon Nelson 	ioread32(&ionic->idev.hwstamp_regs->tick_low);
272fee6efceSShannon Nelson 
273fee6efceSShannon Nelson 	tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high);
274fee6efceSShannon Nelson 
275fee6efceSShannon Nelson 	ptp_read_system_prets(sts);
276fee6efceSShannon Nelson 	tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
277fee6efceSShannon Nelson 	ptp_read_system_postts(sts);
278fee6efceSShannon Nelson 
279fee6efceSShannon Nelson 	tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high);
280fee6efceSShannon Nelson 
281fee6efceSShannon Nelson 	/* If tick_high changed, re-read tick_low once more.  Assume tick_high
282fee6efceSShannon Nelson 	 * cannot change again so soon as in the span of re-reading tick_low.
283fee6efceSShannon Nelson 	 */
284fee6efceSShannon Nelson 	if (tick_high != tick_high_before) {
285fee6efceSShannon Nelson 		ptp_read_system_prets(sts);
286fee6efceSShannon Nelson 		tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
287fee6efceSShannon Nelson 		ptp_read_system_postts(sts);
288fee6efceSShannon Nelson 	}
289fee6efceSShannon Nelson 
290fee6efceSShannon Nelson 	return (u64)tick_low | ((u64)tick_high << 32);
291fee6efceSShannon Nelson }
292fee6efceSShannon Nelson 
ionic_cc_read(const struct cyclecounter * cc)293fee6efceSShannon Nelson static u64 ionic_cc_read(const struct cyclecounter *cc)
294fee6efceSShannon Nelson {
295fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc);
296fee6efceSShannon Nelson 	struct ionic *ionic = phc->lif->ionic;
297fee6efceSShannon Nelson 
298fee6efceSShannon Nelson 	return ionic_hwstamp_read(ionic, NULL);
299fee6efceSShannon Nelson }
300fee6efceSShannon Nelson 
ionic_setphc_cmd(struct ionic_phc * phc,struct ionic_admin_ctx * ctx)301fee6efceSShannon Nelson static int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx)
302fee6efceSShannon Nelson {
303fee6efceSShannon Nelson 	ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work);
304fee6efceSShannon Nelson 
305fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC;
306fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index);
307fee6efceSShannon Nelson 
308fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last);
309fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec);
310fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac);
311fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult);
312fee6efceSShannon Nelson 	ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift);
313fee6efceSShannon Nelson 
314fee6efceSShannon Nelson 	return ionic_adminq_post(phc->lif, ctx);
315fee6efceSShannon Nelson }
316fee6efceSShannon Nelson 
ionic_phc_adjfine(struct ptp_clock_info * info,long scaled_ppm)317fee6efceSShannon Nelson static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
318fee6efceSShannon Nelson {
319fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
320fee6efceSShannon Nelson 	struct ionic_admin_ctx ctx = {};
321fee6efceSShannon Nelson 	unsigned long irqflags;
322fee6efceSShannon Nelson 	s64 adj;
323fee6efceSShannon Nelson 	int err;
324fee6efceSShannon Nelson 
325fee6efceSShannon Nelson 	/* Reject phc adjustments during device upgrade */
326fee6efceSShannon Nelson 	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
327fee6efceSShannon Nelson 		return -EBUSY;
328fee6efceSShannon Nelson 
329fee6efceSShannon Nelson 	/* Adjustment value scaled by 2^16 million */
330fee6efceSShannon Nelson 	adj = (s64)scaled_ppm * phc->init_cc_mult;
331fee6efceSShannon Nelson 
332fee6efceSShannon Nelson 	/* Adjustment value to scale */
333fee6efceSShannon Nelson 	adj /= (s64)SCALED_PPM;
334fee6efceSShannon Nelson 
335fee6efceSShannon Nelson 	/* Final adjusted multiplier */
336fee6efceSShannon Nelson 	adj += phc->init_cc_mult;
337fee6efceSShannon Nelson 
338fee6efceSShannon Nelson 	spin_lock_irqsave(&phc->lock, irqflags);
339fee6efceSShannon Nelson 
340fee6efceSShannon Nelson 	/* update the point-in-time basis to now, before adjusting the rate */
341fee6efceSShannon Nelson 	timecounter_read(&phc->tc);
342fee6efceSShannon Nelson 	phc->cc.mult = adj;
343fee6efceSShannon Nelson 
344fee6efceSShannon Nelson 	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
345fee6efceSShannon Nelson 	 * need to drop the lock before waiting for the command to complete.
346fee6efceSShannon Nelson 	 */
347fee6efceSShannon Nelson 	err = ionic_setphc_cmd(phc, &ctx);
348fee6efceSShannon Nelson 
349fee6efceSShannon Nelson 	spin_unlock_irqrestore(&phc->lock, irqflags);
350fee6efceSShannon Nelson 
3518c9d956aSShannon Nelson 	return ionic_adminq_wait(phc->lif, &ctx, err, true);
352fee6efceSShannon Nelson }
353fee6efceSShannon Nelson 
ionic_phc_adjtime(struct ptp_clock_info * info,s64 delta)354fee6efceSShannon Nelson static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta)
355fee6efceSShannon Nelson {
356fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
357fee6efceSShannon Nelson 	struct ionic_admin_ctx ctx = {};
358fee6efceSShannon Nelson 	unsigned long irqflags;
359fee6efceSShannon Nelson 	int err;
360fee6efceSShannon Nelson 
361fee6efceSShannon Nelson 	/* Reject phc adjustments during device upgrade */
362fee6efceSShannon Nelson 	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
363fee6efceSShannon Nelson 		return -EBUSY;
364fee6efceSShannon Nelson 
365fee6efceSShannon Nelson 	spin_lock_irqsave(&phc->lock, irqflags);
366fee6efceSShannon Nelson 
367fee6efceSShannon Nelson 	timecounter_adjtime(&phc->tc, delta);
368fee6efceSShannon Nelson 
369fee6efceSShannon Nelson 	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
370fee6efceSShannon Nelson 	 * need to drop the lock before waiting for the command to complete.
371fee6efceSShannon Nelson 	 */
372fee6efceSShannon Nelson 	err = ionic_setphc_cmd(phc, &ctx);
373fee6efceSShannon Nelson 
374fee6efceSShannon Nelson 	spin_unlock_irqrestore(&phc->lock, irqflags);
375fee6efceSShannon Nelson 
3768c9d956aSShannon Nelson 	return ionic_adminq_wait(phc->lif, &ctx, err, true);
377fee6efceSShannon Nelson }
378fee6efceSShannon Nelson 
ionic_phc_settime64(struct ptp_clock_info * info,const struct timespec64 * ts)379fee6efceSShannon Nelson static int ionic_phc_settime64(struct ptp_clock_info *info,
380fee6efceSShannon Nelson 			       const struct timespec64 *ts)
381fee6efceSShannon Nelson {
382fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
383fee6efceSShannon Nelson 	struct ionic_admin_ctx ctx = {};
384fee6efceSShannon Nelson 	unsigned long irqflags;
385fee6efceSShannon Nelson 	int err;
386fee6efceSShannon Nelson 	u64 ns;
387fee6efceSShannon Nelson 
388fee6efceSShannon Nelson 	/* Reject phc adjustments during device upgrade */
389fee6efceSShannon Nelson 	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
390fee6efceSShannon Nelson 		return -EBUSY;
391fee6efceSShannon Nelson 
392fee6efceSShannon Nelson 	ns = timespec64_to_ns(ts);
393fee6efceSShannon Nelson 
394fee6efceSShannon Nelson 	spin_lock_irqsave(&phc->lock, irqflags);
395fee6efceSShannon Nelson 
396fee6efceSShannon Nelson 	timecounter_init(&phc->tc, &phc->cc, ns);
397fee6efceSShannon Nelson 
398fee6efceSShannon Nelson 	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
399fee6efceSShannon Nelson 	 * need to drop the lock before waiting for the command to complete.
400fee6efceSShannon Nelson 	 */
401fee6efceSShannon Nelson 	err = ionic_setphc_cmd(phc, &ctx);
402fee6efceSShannon Nelson 
403fee6efceSShannon Nelson 	spin_unlock_irqrestore(&phc->lock, irqflags);
404fee6efceSShannon Nelson 
4058c9d956aSShannon Nelson 	return ionic_adminq_wait(phc->lif, &ctx, err, true);
406fee6efceSShannon Nelson }
407fee6efceSShannon Nelson 
ionic_phc_gettimex64(struct ptp_clock_info * info,struct timespec64 * ts,struct ptp_system_timestamp * sts)408fee6efceSShannon Nelson static int ionic_phc_gettimex64(struct ptp_clock_info *info,
409fee6efceSShannon Nelson 				struct timespec64 *ts,
410fee6efceSShannon Nelson 				struct ptp_system_timestamp *sts)
411fee6efceSShannon Nelson {
412fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
413fee6efceSShannon Nelson 	struct ionic *ionic = phc->lif->ionic;
414fee6efceSShannon Nelson 	unsigned long irqflags;
415fee6efceSShannon Nelson 	u64 tick, ns;
416fee6efceSShannon Nelson 
417fee6efceSShannon Nelson 	/* Do not attempt to read device time during upgrade */
418fee6efceSShannon Nelson 	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
419fee6efceSShannon Nelson 		return -EBUSY;
420fee6efceSShannon Nelson 
421fee6efceSShannon Nelson 	spin_lock_irqsave(&phc->lock, irqflags);
422fee6efceSShannon Nelson 
423fee6efceSShannon Nelson 	tick = ionic_hwstamp_read(ionic, sts);
424fee6efceSShannon Nelson 
425fee6efceSShannon Nelson 	ns = timecounter_cyc2time(&phc->tc, tick);
426fee6efceSShannon Nelson 
427fee6efceSShannon Nelson 	spin_unlock_irqrestore(&phc->lock, irqflags);
428fee6efceSShannon Nelson 
429fee6efceSShannon Nelson 	*ts = ns_to_timespec64(ns);
430fee6efceSShannon Nelson 
431fee6efceSShannon Nelson 	return 0;
432fee6efceSShannon Nelson }
433fee6efceSShannon Nelson 
ionic_phc_aux_work(struct ptp_clock_info * info)434fee6efceSShannon Nelson static long ionic_phc_aux_work(struct ptp_clock_info *info)
435fee6efceSShannon Nelson {
436fee6efceSShannon Nelson 	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
437fee6efceSShannon Nelson 	struct ionic_admin_ctx ctx = {};
438fee6efceSShannon Nelson 	unsigned long irqflags;
439fee6efceSShannon Nelson 	int err;
440fee6efceSShannon Nelson 
441fee6efceSShannon Nelson 	/* Do not update phc during device upgrade, but keep polling to resume
442fee6efceSShannon Nelson 	 * after upgrade.  Since we don't update the point in time basis, there
443fee6efceSShannon Nelson 	 * is no expectation that we are maintaining the phc time during the
444fee6efceSShannon Nelson 	 * upgrade.  After upgrade, it will need to be readjusted back to the
445fee6efceSShannon Nelson 	 * correct time by the ptp daemon.
446fee6efceSShannon Nelson 	 */
447fee6efceSShannon Nelson 	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
448fee6efceSShannon Nelson 		return phc->aux_work_delay;
449fee6efceSShannon Nelson 
450fee6efceSShannon Nelson 	spin_lock_irqsave(&phc->lock, irqflags);
451fee6efceSShannon Nelson 
452fee6efceSShannon Nelson 	/* update point-in-time basis to now */
453fee6efceSShannon Nelson 	timecounter_read(&phc->tc);
454fee6efceSShannon Nelson 
455fee6efceSShannon Nelson 	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
456fee6efceSShannon Nelson 	 * need to drop the lock before waiting for the command to complete.
457fee6efceSShannon Nelson 	 */
458fee6efceSShannon Nelson 	err = ionic_setphc_cmd(phc, &ctx);
459fee6efceSShannon Nelson 
460fee6efceSShannon Nelson 	spin_unlock_irqrestore(&phc->lock, irqflags);
461fee6efceSShannon Nelson 
4628c9d956aSShannon Nelson 	ionic_adminq_wait(phc->lif, &ctx, err, true);
463fee6efceSShannon Nelson 
464fee6efceSShannon Nelson 	return phc->aux_work_delay;
465fee6efceSShannon Nelson }
466fee6efceSShannon Nelson 
ionic_lif_phc_ktime(struct ionic_lif * lif,u64 tick)467fee6efceSShannon Nelson ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 tick)
468fee6efceSShannon Nelson {
469fee6efceSShannon Nelson 	unsigned long irqflags;
470fee6efceSShannon Nelson 	u64 ns;
471fee6efceSShannon Nelson 
472fee6efceSShannon Nelson 	if (!lif->phc)
473fee6efceSShannon Nelson 		return 0;
474fee6efceSShannon Nelson 
475fee6efceSShannon Nelson 	spin_lock_irqsave(&lif->phc->lock, irqflags);
476fee6efceSShannon Nelson 	ns = timecounter_cyc2time(&lif->phc->tc, tick);
477fee6efceSShannon Nelson 	spin_unlock_irqrestore(&lif->phc->lock, irqflags);
478fee6efceSShannon Nelson 
479fee6efceSShannon Nelson 	return ns_to_ktime(ns);
480fee6efceSShannon Nelson }
481fee6efceSShannon Nelson 
482fee6efceSShannon Nelson static const struct ptp_clock_info ionic_ptp_info = {
483fee6efceSShannon Nelson 	.owner		= THIS_MODULE,
484fee6efceSShannon Nelson 	.name		= "ionic_ptp",
485fee6efceSShannon Nelson 	.adjfine	= ionic_phc_adjfine,
486fee6efceSShannon Nelson 	.adjtime	= ionic_phc_adjtime,
487fee6efceSShannon Nelson 	.gettimex64	= ionic_phc_gettimex64,
488fee6efceSShannon Nelson 	.settime64	= ionic_phc_settime64,
489fee6efceSShannon Nelson 	.do_aux_work	= ionic_phc_aux_work,
490fee6efceSShannon Nelson };
491fee6efceSShannon Nelson 
ionic_lif_register_phc(struct ionic_lif * lif)492fee6efceSShannon Nelson void ionic_lif_register_phc(struct ionic_lif *lif)
493fee6efceSShannon Nelson {
494fee6efceSShannon Nelson 	if (!lif->phc || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP))
495fee6efceSShannon Nelson 		return;
496fee6efceSShannon Nelson 
497fee6efceSShannon Nelson 	lif->phc->ptp = ptp_clock_register(&lif->phc->ptp_info, lif->ionic->dev);
498fee6efceSShannon Nelson 
499fee6efceSShannon Nelson 	if (IS_ERR(lif->phc->ptp)) {
500fee6efceSShannon Nelson 		dev_warn(lif->ionic->dev, "Cannot register phc device: %ld\n",
501fee6efceSShannon Nelson 			 PTR_ERR(lif->phc->ptp));
502fee6efceSShannon Nelson 
503fee6efceSShannon Nelson 		lif->phc->ptp = NULL;
504fee6efceSShannon Nelson 	}
505fee6efceSShannon Nelson 
506fee6efceSShannon Nelson 	if (lif->phc->ptp)
507fee6efceSShannon Nelson 		ptp_schedule_worker(lif->phc->ptp, lif->phc->aux_work_delay);
508fee6efceSShannon Nelson }
509fee6efceSShannon Nelson 
ionic_lif_unregister_phc(struct ionic_lif * lif)510fee6efceSShannon Nelson void ionic_lif_unregister_phc(struct ionic_lif *lif)
511fee6efceSShannon Nelson {
512fee6efceSShannon Nelson 	if (!lif->phc || !lif->phc->ptp)
513fee6efceSShannon Nelson 		return;
514fee6efceSShannon Nelson 
515fee6efceSShannon Nelson 	ptp_clock_unregister(lif->phc->ptp);
516fee6efceSShannon Nelson 
517fee6efceSShannon Nelson 	lif->phc->ptp = NULL;
518fee6efceSShannon Nelson }
519fee6efceSShannon Nelson 
ionic_lif_alloc_phc(struct ionic_lif * lif)520fee6efceSShannon Nelson void ionic_lif_alloc_phc(struct ionic_lif *lif)
521fee6efceSShannon Nelson {
522fee6efceSShannon Nelson 	struct ionic *ionic = lif->ionic;
523fee6efceSShannon Nelson 	struct ionic_phc *phc;
524fee6efceSShannon Nelson 	u64 delay, diff, mult;
525fee6efceSShannon Nelson 	u64 frac = 0;
526fee6efceSShannon Nelson 	u64 features;
527fee6efceSShannon Nelson 	u32 shift;
528fee6efceSShannon Nelson 
529fee6efceSShannon Nelson 	if (!ionic->idev.hwstamp_regs)
530fee6efceSShannon Nelson 		return;
531fee6efceSShannon Nelson 
532fee6efceSShannon Nelson 	features = le64_to_cpu(ionic->ident.lif.eth.config.features);
533fee6efceSShannon Nelson 	if (!(features & IONIC_ETH_HW_TIMESTAMP))
534fee6efceSShannon Nelson 		return;
535fee6efceSShannon Nelson 
536fee6efceSShannon Nelson 	phc = devm_kzalloc(ionic->dev, sizeof(*phc), GFP_KERNEL);
537fee6efceSShannon Nelson 	if (!phc)
538fee6efceSShannon Nelson 		return;
539fee6efceSShannon Nelson 
540fee6efceSShannon Nelson 	phc->lif = lif;
541fee6efceSShannon Nelson 
542fee6efceSShannon Nelson 	phc->cc.read = ionic_cc_read;
543fee6efceSShannon Nelson 	phc->cc.mask = le64_to_cpu(ionic->ident.dev.hwstamp_mask);
544fee6efceSShannon Nelson 	phc->cc.mult = le32_to_cpu(ionic->ident.dev.hwstamp_mult);
545fee6efceSShannon Nelson 	phc->cc.shift = le32_to_cpu(ionic->ident.dev.hwstamp_shift);
546fee6efceSShannon Nelson 
547fee6efceSShannon Nelson 	if (!phc->cc.mult) {
548fee6efceSShannon Nelson 		dev_err(lif->ionic->dev,
549fee6efceSShannon Nelson 			"Invalid device PHC mask multiplier %u, disabling HW timestamp support\n",
550fee6efceSShannon Nelson 			phc->cc.mult);
551fee6efceSShannon Nelson 		devm_kfree(lif->ionic->dev, phc);
552fee6efceSShannon Nelson 		lif->phc = NULL;
553fee6efceSShannon Nelson 		return;
554fee6efceSShannon Nelson 	}
555fee6efceSShannon Nelson 
556fee6efceSShannon Nelson 	dev_dbg(lif->ionic->dev, "Device PHC mask %#llx mult %u shift %u\n",
557fee6efceSShannon Nelson 		phc->cc.mask, phc->cc.mult, phc->cc.shift);
558fee6efceSShannon Nelson 
559fee6efceSShannon Nelson 	spin_lock_init(&phc->lock);
560fee6efceSShannon Nelson 	mutex_init(&phc->config_lock);
561fee6efceSShannon Nelson 
562fee6efceSShannon Nelson 	/* max ticks is limited by the multiplier, or by the update period. */
563fee6efceSShannon Nelson 	if (phc->cc.shift + 2 + ilog2(IONIC_PHC_UPDATE_NS) >= 64) {
564fee6efceSShannon Nelson 		/* max ticks that do not overflow when multiplied by max
565fee6efceSShannon Nelson 		 * adjusted multiplier (twice the initial multiplier)
566fee6efceSShannon Nelson 		 */
567fee6efceSShannon Nelson 		diff = U64_MAX / phc->cc.mult / 2;
568fee6efceSShannon Nelson 	} else {
569fee6efceSShannon Nelson 		/* approx ticks at four times the update period */
570fee6efceSShannon Nelson 		diff = (u64)IONIC_PHC_UPDATE_NS << (phc->cc.shift + 2);
571fee6efceSShannon Nelson 		diff = DIV_ROUND_UP(diff, phc->cc.mult);
572fee6efceSShannon Nelson 	}
573fee6efceSShannon Nelson 
574fee6efceSShannon Nelson 	/* transform to bitmask */
575fee6efceSShannon Nelson 	diff |= diff >> 1;
576fee6efceSShannon Nelson 	diff |= diff >> 2;
577fee6efceSShannon Nelson 	diff |= diff >> 4;
578fee6efceSShannon Nelson 	diff |= diff >> 8;
579fee6efceSShannon Nelson 	diff |= diff >> 16;
580fee6efceSShannon Nelson 	diff |= diff >> 32;
581fee6efceSShannon Nelson 
582*be690daaSBrett Creeley 	/* constrain to the hardware bitmask */
583fee6efceSShannon Nelson 	diff &= phc->cc.mask;
584fee6efceSShannon Nelson 
585*be690daaSBrett Creeley 	/* the wrap period is now defined by diff
586fee6efceSShannon Nelson 	 *
587fee6efceSShannon Nelson 	 * we will update the time basis at about 1/4 the wrap period, so
588fee6efceSShannon Nelson 	 * should not see a difference of more than +/- diff/4.
589fee6efceSShannon Nelson 	 *
590fee6efceSShannon Nelson 	 * this is sufficient not see a difference of more than +/- diff/2, as
591fee6efceSShannon Nelson 	 * required by timecounter_cyc2time, to detect an old time stamp.
592fee6efceSShannon Nelson 	 *
593fee6efceSShannon Nelson 	 * adjust the initial multiplier, being careful to avoid overflow:
594fee6efceSShannon Nelson 	 *  - do not overflow 63 bits: init_cc_mult * SCALED_PPM
595fee6efceSShannon Nelson 	 *  - do not overflow 64 bits: max_mult * (diff / 2)
596fee6efceSShannon Nelson 	 *
597fee6efceSShannon Nelson 	 * we want to increase the initial multiplier as much as possible, to
598fee6efceSShannon Nelson 	 * allow for more precise adjustment in ionic_phc_adjfine.
599fee6efceSShannon Nelson 	 *
600fee6efceSShannon Nelson 	 * only adjust the multiplier if we can double it or more.
601fee6efceSShannon Nelson 	 */
602fee6efceSShannon Nelson 	mult = U64_MAX / 2 / max(diff / 2, SCALED_PPM);
603fee6efceSShannon Nelson 	shift = mult / phc->cc.mult;
604fee6efceSShannon Nelson 	if (shift >= 2) {
605fee6efceSShannon Nelson 		/* initial multiplier will be 2^n of hardware cc.mult */
606fee6efceSShannon Nelson 		shift = fls(shift);
607fee6efceSShannon Nelson 		/* increase cc.mult and cc.shift by the same 2^n and n. */
608fee6efceSShannon Nelson 		phc->cc.mult <<= shift;
609fee6efceSShannon Nelson 		phc->cc.shift += shift;
610fee6efceSShannon Nelson 	}
611fee6efceSShannon Nelson 
612fee6efceSShannon Nelson 	dev_dbg(lif->ionic->dev, "Initial PHC mask %#llx mult %u shift %u\n",
613fee6efceSShannon Nelson 		phc->cc.mask, phc->cc.mult, phc->cc.shift);
614fee6efceSShannon Nelson 
615fee6efceSShannon Nelson 	/* frequency adjustments are relative to the initial multiplier */
616fee6efceSShannon Nelson 	phc->init_cc_mult = phc->cc.mult;
617fee6efceSShannon Nelson 
618fee6efceSShannon Nelson 	timecounter_init(&phc->tc, &phc->cc, ktime_get_real_ns());
619fee6efceSShannon Nelson 
620fee6efceSShannon Nelson 	/* Update cycle_last at 1/4 the wrap period, or IONIC_PHC_UPDATE_NS */
621fee6efceSShannon Nelson 	delay = min_t(u64, IONIC_PHC_UPDATE_NS,
622fee6efceSShannon Nelson 		      cyclecounter_cyc2ns(&phc->cc, diff / 4, 0, &frac));
623fee6efceSShannon Nelson 	dev_dbg(lif->ionic->dev, "Work delay %llu ms\n", delay / NSEC_PER_MSEC);
624fee6efceSShannon Nelson 
625fee6efceSShannon Nelson 	phc->aux_work_delay = nsecs_to_jiffies(delay);
626fee6efceSShannon Nelson 
627fee6efceSShannon Nelson 	phc->ptp_info = ionic_ptp_info;
628fee6efceSShannon Nelson 
629fee6efceSShannon Nelson 	/* We have allowed to adjust the multiplier up to +/- 1 part per 1.
630fee6efceSShannon Nelson 	 * Here expressed as NORMAL_PPB (1 billion parts per billion).
631fee6efceSShannon Nelson 	 */
632fee6efceSShannon Nelson 	phc->ptp_info.max_adj = NORMAL_PPB;
633fee6efceSShannon Nelson 
634fee6efceSShannon Nelson 	lif->phc = phc;
635fee6efceSShannon Nelson }
636fee6efceSShannon Nelson 
ionic_lif_free_phc(struct ionic_lif * lif)637fee6efceSShannon Nelson void ionic_lif_free_phc(struct ionic_lif *lif)
638fee6efceSShannon Nelson {
639fee6efceSShannon Nelson 	if (!lif->phc)
640fee6efceSShannon Nelson 		return;
641fee6efceSShannon Nelson 
642fee6efceSShannon Nelson 	mutex_destroy(&lif->phc->config_lock);
643fee6efceSShannon Nelson 
644fee6efceSShannon Nelson 	devm_kfree(lif->ionic->dev, lif->phc);
645fee6efceSShannon Nelson 	lif->phc = NULL;
646fee6efceSShannon Nelson }
647