162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2017 - 2021 Pensando Systems, Inc */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/netdevice.h>
562306a36Sopenharmony_ci#include <linux/etherdevice.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "ionic.h"
862306a36Sopenharmony_ci#include "ionic_bus.h"
962306a36Sopenharmony_ci#include "ionic_lif.h"
1062306a36Sopenharmony_ci#include "ionic_ethtool.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic int ionic_hwstamp_tx_mode(int config_tx_type)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	switch (config_tx_type) {
1562306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
1662306a36Sopenharmony_ci		return IONIC_TXSTAMP_OFF;
1762306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
1862306a36Sopenharmony_ci		return IONIC_TXSTAMP_ON;
1962306a36Sopenharmony_ci	case HWTSTAMP_TX_ONESTEP_SYNC:
2062306a36Sopenharmony_ci		return IONIC_TXSTAMP_ONESTEP_SYNC;
2162306a36Sopenharmony_ci	case HWTSTAMP_TX_ONESTEP_P2P:
2262306a36Sopenharmony_ci		return IONIC_TXSTAMP_ONESTEP_P2P;
2362306a36Sopenharmony_ci	default:
2462306a36Sopenharmony_ci		return -ERANGE;
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic u64 ionic_hwstamp_rx_filt(int config_rx_filter)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	switch (config_rx_filter) {
3162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
3262306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP1_ALL;
3362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
3462306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP1_SYNC;
3562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
3662306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
3962306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L4_ALL;
4062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
4162306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L4_SYNC;
4262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
4362306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
4662306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L2_ALL;
4762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
4862306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L2_SYNC;
4962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
5062306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
5362306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_ALL;
5462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
5562306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_SYNC;
5662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
5762306a36Sopenharmony_ci		return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
6062306a36Sopenharmony_ci		return IONIC_PKT_CLS_NTP_ALL;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	default:
6362306a36Sopenharmony_ci		return 0;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
6862306a36Sopenharmony_ci					   struct hwtstamp_config *new_ts)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct ionic *ionic = lif->ionic;
7162306a36Sopenharmony_ci	struct hwtstamp_config *config;
7262306a36Sopenharmony_ci	struct hwtstamp_config ts;
7362306a36Sopenharmony_ci	int tx_mode = 0;
7462306a36Sopenharmony_ci	u64 rx_filt = 0;
7562306a36Sopenharmony_ci	int err, err2;
7662306a36Sopenharmony_ci	bool rx_all;
7762306a36Sopenharmony_ci	__le64 mask;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
8062306a36Sopenharmony_ci		return -EOPNOTSUPP;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	mutex_lock(&lif->phc->config_lock);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (new_ts) {
8562306a36Sopenharmony_ci		config = new_ts;
8662306a36Sopenharmony_ci	} else {
8762306a36Sopenharmony_ci		/* If called with new_ts == NULL, replay the previous request
8862306a36Sopenharmony_ci		 * primarily for recovery after a FW_RESET.
8962306a36Sopenharmony_ci		 * We saved the previous configuration request info, so copy
9062306a36Sopenharmony_ci		 * the previous request for reference, clear the current state
9162306a36Sopenharmony_ci		 * to match the device's reset state, and run with it.
9262306a36Sopenharmony_ci		 */
9362306a36Sopenharmony_ci		config = &ts;
9462306a36Sopenharmony_ci		memcpy(config, &lif->phc->ts_config, sizeof(*config));
9562306a36Sopenharmony_ci		memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config));
9662306a36Sopenharmony_ci		lif->phc->ts_config_tx_mode = 0;
9762306a36Sopenharmony_ci		lif->phc->ts_config_rx_filt = 0;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	tx_mode = ionic_hwstamp_tx_mode(config->tx_type);
10162306a36Sopenharmony_ci	if (tx_mode < 0) {
10262306a36Sopenharmony_ci		err = tx_mode;
10362306a36Sopenharmony_ci		goto err_queues;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	mask = cpu_to_le64(BIT_ULL(tx_mode));
10762306a36Sopenharmony_ci	if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) {
10862306a36Sopenharmony_ci		err = -ERANGE;
10962306a36Sopenharmony_ci		goto err_queues;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	rx_filt = ionic_hwstamp_rx_filt(config->rx_filter);
11362306a36Sopenharmony_ci	rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	mask = cpu_to_le64(rx_filt);
11662306a36Sopenharmony_ci	if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) {
11762306a36Sopenharmony_ci		rx_filt = 0;
11862306a36Sopenharmony_ci		rx_all = true;
11962306a36Sopenharmony_ci		config->rx_filter = HWTSTAMP_FILTER_ALL;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n",
12362306a36Sopenharmony_ci		__func__, config->rx_filter, rx_filt, rx_all);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (tx_mode) {
12662306a36Sopenharmony_ci		err = ionic_lif_create_hwstamp_txq(lif);
12762306a36Sopenharmony_ci		if (err)
12862306a36Sopenharmony_ci			goto err_queues;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (rx_filt) {
13262306a36Sopenharmony_ci		err = ionic_lif_create_hwstamp_rxq(lif);
13362306a36Sopenharmony_ci		if (err)
13462306a36Sopenharmony_ci			goto err_queues;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (tx_mode != lif->phc->ts_config_tx_mode) {
13862306a36Sopenharmony_ci		err = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
13962306a36Sopenharmony_ci		if (err)
14062306a36Sopenharmony_ci			goto err_txmode;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (rx_filt != lif->phc->ts_config_rx_filt) {
14462306a36Sopenharmony_ci		err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
14562306a36Sopenharmony_ci		if (err)
14662306a36Sopenharmony_ci			goto err_rxfilt;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) {
15062306a36Sopenharmony_ci		err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all);
15162306a36Sopenharmony_ci		if (err)
15262306a36Sopenharmony_ci			goto err_rxall;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	memcpy(&lif->phc->ts_config, config, sizeof(*config));
15662306a36Sopenharmony_ci	lif->phc->ts_config_rx_filt = rx_filt;
15762306a36Sopenharmony_ci	lif->phc->ts_config_tx_mode = tx_mode;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	mutex_unlock(&lif->phc->config_lock);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cierr_rxall:
16462306a36Sopenharmony_ci	if (rx_filt != lif->phc->ts_config_rx_filt) {
16562306a36Sopenharmony_ci		rx_filt = lif->phc->ts_config_rx_filt;
16662306a36Sopenharmony_ci		err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
16762306a36Sopenharmony_ci		if (err2)
16862306a36Sopenharmony_ci			dev_err(ionic->dev,
16962306a36Sopenharmony_ci				"Failed to revert rx timestamp filter: %d\n", err2);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_cierr_rxfilt:
17262306a36Sopenharmony_ci	if (tx_mode != lif->phc->ts_config_tx_mode) {
17362306a36Sopenharmony_ci		tx_mode = lif->phc->ts_config_tx_mode;
17462306a36Sopenharmony_ci		err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
17562306a36Sopenharmony_ci		if (err2)
17662306a36Sopenharmony_ci			dev_err(ionic->dev,
17762306a36Sopenharmony_ci				"Failed to revert tx timestamp mode: %d\n", err2);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_cierr_txmode:
18062306a36Sopenharmony_ci	/* special queues remain allocated, just unused */
18162306a36Sopenharmony_cierr_queues:
18262306a36Sopenharmony_ci	mutex_unlock(&lif->phc->config_lock);
18362306a36Sopenharmony_ci	return err;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciint ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct hwtstamp_config config;
18962306a36Sopenharmony_ci	int err;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
19262306a36Sopenharmony_ci		return -EOPNOTSUPP;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
19562306a36Sopenharmony_ci		return -EFAULT;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	mutex_lock(&lif->queue_lock);
19862306a36Sopenharmony_ci	err = ionic_lif_hwstamp_set_ts_config(lif, &config);
19962306a36Sopenharmony_ci	mutex_unlock(&lif->queue_lock);
20062306a36Sopenharmony_ci	if (err) {
20162306a36Sopenharmony_ci		netdev_info(lif->netdev, "hwstamp set failed: %d\n", err);
20262306a36Sopenharmony_ci		return err;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
20662306a36Sopenharmony_ci		return -EFAULT;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid ionic_lif_hwstamp_replay(struct ionic_lif *lif)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int err;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
21662306a36Sopenharmony_ci		return;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	mutex_lock(&lif->queue_lock);
21962306a36Sopenharmony_ci	err = ionic_lif_hwstamp_set_ts_config(lif, NULL);
22062306a36Sopenharmony_ci	mutex_unlock(&lif->queue_lock);
22162306a36Sopenharmony_ci	if (err)
22262306a36Sopenharmony_ci		netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_civoid ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	int err;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
23062306a36Sopenharmony_ci		return;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	mutex_lock(&lif->phc->config_lock);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (lif->phc->ts_config_tx_mode) {
23562306a36Sopenharmony_ci		err = ionic_lif_create_hwstamp_txq(lif);
23662306a36Sopenharmony_ci		if (err)
23762306a36Sopenharmony_ci			netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (lif->phc->ts_config_rx_filt) {
24162306a36Sopenharmony_ci		err = ionic_lif_create_hwstamp_rxq(lif);
24262306a36Sopenharmony_ci		if (err)
24362306a36Sopenharmony_ci			netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	mutex_unlock(&lif->phc->config_lock);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct hwtstamp_config config;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
25462306a36Sopenharmony_ci		return -EOPNOTSUPP;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	mutex_lock(&lif->phc->config_lock);
25762306a36Sopenharmony_ci	memcpy(&config, &lif->phc->ts_config, sizeof(config));
25862306a36Sopenharmony_ci	mutex_unlock(&lif->phc->config_lock);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
26162306a36Sopenharmony_ci		return -EFAULT;
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic u64 ionic_hwstamp_read(struct ionic *ionic,
26662306a36Sopenharmony_ci			      struct ptp_system_timestamp *sts)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	u32 tick_high_before, tick_high, tick_low;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* read and discard low part to defeat hw staging of high part */
27162306a36Sopenharmony_ci	ioread32(&ionic->idev.hwstamp_regs->tick_low);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	ptp_read_system_prets(sts);
27662306a36Sopenharmony_ci	tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
27762306a36Sopenharmony_ci	ptp_read_system_postts(sts);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* If tick_high changed, re-read tick_low once more.  Assume tick_high
28262306a36Sopenharmony_ci	 * cannot change again so soon as in the span of re-reading tick_low.
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci	if (tick_high != tick_high_before) {
28562306a36Sopenharmony_ci		ptp_read_system_prets(sts);
28662306a36Sopenharmony_ci		tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
28762306a36Sopenharmony_ci		ptp_read_system_postts(sts);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return (u64)tick_low | ((u64)tick_high << 32);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic u64 ionic_cc_read(const struct cyclecounter *cc)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc);
29662306a36Sopenharmony_ci	struct ionic *ionic = phc->lif->ionic;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return ionic_hwstamp_read(ionic, NULL);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC;
30662306a36Sopenharmony_ci	ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last);
30962306a36Sopenharmony_ci	ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec);
31062306a36Sopenharmony_ci	ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac);
31162306a36Sopenharmony_ci	ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult);
31262306a36Sopenharmony_ci	ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return ionic_adminq_post(phc->lif, ctx);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
32062306a36Sopenharmony_ci	struct ionic_admin_ctx ctx = {};
32162306a36Sopenharmony_ci	unsigned long irqflags;
32262306a36Sopenharmony_ci	s64 adj;
32362306a36Sopenharmony_ci	int err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Reject phc adjustments during device upgrade */
32662306a36Sopenharmony_ci	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
32762306a36Sopenharmony_ci		return -EBUSY;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* Adjustment value scaled by 2^16 million */
33062306a36Sopenharmony_ci	adj = (s64)scaled_ppm * phc->init_cc_mult;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Adjustment value to scale */
33362306a36Sopenharmony_ci	adj /= (s64)SCALED_PPM;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Final adjusted multiplier */
33662306a36Sopenharmony_ci	adj += phc->init_cc_mult;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	spin_lock_irqsave(&phc->lock, irqflags);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* update the point-in-time basis to now, before adjusting the rate */
34162306a36Sopenharmony_ci	timecounter_read(&phc->tc);
34262306a36Sopenharmony_ci	phc->cc.mult = adj;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
34562306a36Sopenharmony_ci	 * need to drop the lock before waiting for the command to complete.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	err = ionic_setphc_cmd(phc, &ctx);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_unlock_irqrestore(&phc->lock, irqflags);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return ionic_adminq_wait(phc->lif, &ctx, err, true);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
35762306a36Sopenharmony_ci	struct ionic_admin_ctx ctx = {};
35862306a36Sopenharmony_ci	unsigned long irqflags;
35962306a36Sopenharmony_ci	int err;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* Reject phc adjustments during device upgrade */
36262306a36Sopenharmony_ci	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
36362306a36Sopenharmony_ci		return -EBUSY;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	spin_lock_irqsave(&phc->lock, irqflags);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	timecounter_adjtime(&phc->tc, delta);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
37062306a36Sopenharmony_ci	 * need to drop the lock before waiting for the command to complete.
37162306a36Sopenharmony_ci	 */
37262306a36Sopenharmony_ci	err = ionic_setphc_cmd(phc, &ctx);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	spin_unlock_irqrestore(&phc->lock, irqflags);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return ionic_adminq_wait(phc->lif, &ctx, err, true);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int ionic_phc_settime64(struct ptp_clock_info *info,
38062306a36Sopenharmony_ci			       const struct timespec64 *ts)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
38362306a36Sopenharmony_ci	struct ionic_admin_ctx ctx = {};
38462306a36Sopenharmony_ci	unsigned long irqflags;
38562306a36Sopenharmony_ci	int err;
38662306a36Sopenharmony_ci	u64 ns;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Reject phc adjustments during device upgrade */
38962306a36Sopenharmony_ci	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
39062306a36Sopenharmony_ci		return -EBUSY;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ns = timespec64_to_ns(ts);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	spin_lock_irqsave(&phc->lock, irqflags);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	timecounter_init(&phc->tc, &phc->cc, ns);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
39962306a36Sopenharmony_ci	 * need to drop the lock before waiting for the command to complete.
40062306a36Sopenharmony_ci	 */
40162306a36Sopenharmony_ci	err = ionic_setphc_cmd(phc, &ctx);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spin_unlock_irqrestore(&phc->lock, irqflags);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return ionic_adminq_wait(phc->lif, &ctx, err, true);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int ionic_phc_gettimex64(struct ptp_clock_info *info,
40962306a36Sopenharmony_ci				struct timespec64 *ts,
41062306a36Sopenharmony_ci				struct ptp_system_timestamp *sts)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
41362306a36Sopenharmony_ci	struct ionic *ionic = phc->lif->ionic;
41462306a36Sopenharmony_ci	unsigned long irqflags;
41562306a36Sopenharmony_ci	u64 tick, ns;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Do not attempt to read device time during upgrade */
41862306a36Sopenharmony_ci	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
41962306a36Sopenharmony_ci		return -EBUSY;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	spin_lock_irqsave(&phc->lock, irqflags);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	tick = ionic_hwstamp_read(ionic, sts);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	ns = timecounter_cyc2time(&phc->tc, tick);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	spin_unlock_irqrestore(&phc->lock, irqflags);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic long ionic_phc_aux_work(struct ptp_clock_info *info)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
43762306a36Sopenharmony_ci	struct ionic_admin_ctx ctx = {};
43862306a36Sopenharmony_ci	unsigned long irqflags;
43962306a36Sopenharmony_ci	int err;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* Do not update phc during device upgrade, but keep polling to resume
44262306a36Sopenharmony_ci	 * after upgrade.  Since we don't update the point in time basis, there
44362306a36Sopenharmony_ci	 * is no expectation that we are maintaining the phc time during the
44462306a36Sopenharmony_ci	 * upgrade.  After upgrade, it will need to be readjusted back to the
44562306a36Sopenharmony_ci	 * correct time by the ptp daemon.
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
44862306a36Sopenharmony_ci		return phc->aux_work_delay;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	spin_lock_irqsave(&phc->lock, irqflags);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* update point-in-time basis to now */
45362306a36Sopenharmony_ci	timecounter_read(&phc->tc);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* Setphc commands are posted in-order, sequenced by phc->lock.  We
45662306a36Sopenharmony_ci	 * need to drop the lock before waiting for the command to complete.
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	err = ionic_setphc_cmd(phc, &ctx);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	spin_unlock_irqrestore(&phc->lock, irqflags);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ionic_adminq_wait(phc->lif, &ctx, err, true);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return phc->aux_work_delay;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 tick)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	unsigned long irqflags;
47062306a36Sopenharmony_ci	u64 ns;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!lif->phc)
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	spin_lock_irqsave(&lif->phc->lock, irqflags);
47662306a36Sopenharmony_ci	ns = timecounter_cyc2time(&lif->phc->tc, tick);
47762306a36Sopenharmony_ci	spin_unlock_irqrestore(&lif->phc->lock, irqflags);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return ns_to_ktime(ns);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic const struct ptp_clock_info ionic_ptp_info = {
48362306a36Sopenharmony_ci	.owner		= THIS_MODULE,
48462306a36Sopenharmony_ci	.name		= "ionic_ptp",
48562306a36Sopenharmony_ci	.adjfine	= ionic_phc_adjfine,
48662306a36Sopenharmony_ci	.adjtime	= ionic_phc_adjtime,
48762306a36Sopenharmony_ci	.gettimex64	= ionic_phc_gettimex64,
48862306a36Sopenharmony_ci	.settime64	= ionic_phc_settime64,
48962306a36Sopenharmony_ci	.do_aux_work	= ionic_phc_aux_work,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_civoid ionic_lif_register_phc(struct ionic_lif *lif)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	if (!lif->phc || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP))
49562306a36Sopenharmony_ci		return;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	lif->phc->ptp = ptp_clock_register(&lif->phc->ptp_info, lif->ionic->dev);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (IS_ERR(lif->phc->ptp)) {
50062306a36Sopenharmony_ci		dev_warn(lif->ionic->dev, "Cannot register phc device: %ld\n",
50162306a36Sopenharmony_ci			 PTR_ERR(lif->phc->ptp));
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		lif->phc->ptp = NULL;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (lif->phc->ptp)
50762306a36Sopenharmony_ci		ptp_schedule_worker(lif->phc->ptp, lif->phc->aux_work_delay);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_civoid ionic_lif_unregister_phc(struct ionic_lif *lif)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	if (!lif->phc || !lif->phc->ptp)
51362306a36Sopenharmony_ci		return;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	ptp_clock_unregister(lif->phc->ptp);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	lif->phc->ptp = NULL;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_civoid ionic_lif_alloc_phc(struct ionic_lif *lif)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct ionic *ionic = lif->ionic;
52362306a36Sopenharmony_ci	struct ionic_phc *phc;
52462306a36Sopenharmony_ci	u64 delay, diff, mult;
52562306a36Sopenharmony_ci	u64 frac = 0;
52662306a36Sopenharmony_ci	u64 features;
52762306a36Sopenharmony_ci	u32 shift;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!ionic->idev.hwstamp_regs)
53062306a36Sopenharmony_ci		return;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	features = le64_to_cpu(ionic->ident.lif.eth.config.features);
53362306a36Sopenharmony_ci	if (!(features & IONIC_ETH_HW_TIMESTAMP))
53462306a36Sopenharmony_ci		return;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	phc = devm_kzalloc(ionic->dev, sizeof(*phc), GFP_KERNEL);
53762306a36Sopenharmony_ci	if (!phc)
53862306a36Sopenharmony_ci		return;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	phc->lif = lif;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	phc->cc.read = ionic_cc_read;
54362306a36Sopenharmony_ci	phc->cc.mask = le64_to_cpu(ionic->ident.dev.hwstamp_mask);
54462306a36Sopenharmony_ci	phc->cc.mult = le32_to_cpu(ionic->ident.dev.hwstamp_mult);
54562306a36Sopenharmony_ci	phc->cc.shift = le32_to_cpu(ionic->ident.dev.hwstamp_shift);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (!phc->cc.mult) {
54862306a36Sopenharmony_ci		dev_err(lif->ionic->dev,
54962306a36Sopenharmony_ci			"Invalid device PHC mask multiplier %u, disabling HW timestamp support\n",
55062306a36Sopenharmony_ci			phc->cc.mult);
55162306a36Sopenharmony_ci		devm_kfree(lif->ionic->dev, phc);
55262306a36Sopenharmony_ci		lif->phc = NULL;
55362306a36Sopenharmony_ci		return;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	dev_dbg(lif->ionic->dev, "Device PHC mask %#llx mult %u shift %u\n",
55762306a36Sopenharmony_ci		phc->cc.mask, phc->cc.mult, phc->cc.shift);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	spin_lock_init(&phc->lock);
56062306a36Sopenharmony_ci	mutex_init(&phc->config_lock);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* max ticks is limited by the multiplier, or by the update period. */
56362306a36Sopenharmony_ci	if (phc->cc.shift + 2 + ilog2(IONIC_PHC_UPDATE_NS) >= 64) {
56462306a36Sopenharmony_ci		/* max ticks that do not overflow when multiplied by max
56562306a36Sopenharmony_ci		 * adjusted multiplier (twice the initial multiplier)
56662306a36Sopenharmony_ci		 */
56762306a36Sopenharmony_ci		diff = U64_MAX / phc->cc.mult / 2;
56862306a36Sopenharmony_ci	} else {
56962306a36Sopenharmony_ci		/* approx ticks at four times the update period */
57062306a36Sopenharmony_ci		diff = (u64)IONIC_PHC_UPDATE_NS << (phc->cc.shift + 2);
57162306a36Sopenharmony_ci		diff = DIV_ROUND_UP(diff, phc->cc.mult);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* transform to bitmask */
57562306a36Sopenharmony_ci	diff |= diff >> 1;
57662306a36Sopenharmony_ci	diff |= diff >> 2;
57762306a36Sopenharmony_ci	diff |= diff >> 4;
57862306a36Sopenharmony_ci	diff |= diff >> 8;
57962306a36Sopenharmony_ci	diff |= diff >> 16;
58062306a36Sopenharmony_ci	diff |= diff >> 32;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* constrain to the hardware bitmask */
58362306a36Sopenharmony_ci	diff &= phc->cc.mask;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* the wrap period is now defined by diff
58662306a36Sopenharmony_ci	 *
58762306a36Sopenharmony_ci	 * we will update the time basis at about 1/4 the wrap period, so
58862306a36Sopenharmony_ci	 * should not see a difference of more than +/- diff/4.
58962306a36Sopenharmony_ci	 *
59062306a36Sopenharmony_ci	 * this is sufficient not see a difference of more than +/- diff/2, as
59162306a36Sopenharmony_ci	 * required by timecounter_cyc2time, to detect an old time stamp.
59262306a36Sopenharmony_ci	 *
59362306a36Sopenharmony_ci	 * adjust the initial multiplier, being careful to avoid overflow:
59462306a36Sopenharmony_ci	 *  - do not overflow 63 bits: init_cc_mult * SCALED_PPM
59562306a36Sopenharmony_ci	 *  - do not overflow 64 bits: max_mult * (diff / 2)
59662306a36Sopenharmony_ci	 *
59762306a36Sopenharmony_ci	 * we want to increase the initial multiplier as much as possible, to
59862306a36Sopenharmony_ci	 * allow for more precise adjustment in ionic_phc_adjfine.
59962306a36Sopenharmony_ci	 *
60062306a36Sopenharmony_ci	 * only adjust the multiplier if we can double it or more.
60162306a36Sopenharmony_ci	 */
60262306a36Sopenharmony_ci	mult = U64_MAX / 2 / max(diff / 2, SCALED_PPM);
60362306a36Sopenharmony_ci	shift = mult / phc->cc.mult;
60462306a36Sopenharmony_ci	if (shift >= 2) {
60562306a36Sopenharmony_ci		/* initial multiplier will be 2^n of hardware cc.mult */
60662306a36Sopenharmony_ci		shift = fls(shift);
60762306a36Sopenharmony_ci		/* increase cc.mult and cc.shift by the same 2^n and n. */
60862306a36Sopenharmony_ci		phc->cc.mult <<= shift;
60962306a36Sopenharmony_ci		phc->cc.shift += shift;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	dev_dbg(lif->ionic->dev, "Initial PHC mask %#llx mult %u shift %u\n",
61362306a36Sopenharmony_ci		phc->cc.mask, phc->cc.mult, phc->cc.shift);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* frequency adjustments are relative to the initial multiplier */
61662306a36Sopenharmony_ci	phc->init_cc_mult = phc->cc.mult;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	timecounter_init(&phc->tc, &phc->cc, ktime_get_real_ns());
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Update cycle_last at 1/4 the wrap period, or IONIC_PHC_UPDATE_NS */
62162306a36Sopenharmony_ci	delay = min_t(u64, IONIC_PHC_UPDATE_NS,
62262306a36Sopenharmony_ci		      cyclecounter_cyc2ns(&phc->cc, diff / 4, 0, &frac));
62362306a36Sopenharmony_ci	dev_dbg(lif->ionic->dev, "Work delay %llu ms\n", delay / NSEC_PER_MSEC);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	phc->aux_work_delay = nsecs_to_jiffies(delay);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	phc->ptp_info = ionic_ptp_info;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* We have allowed to adjust the multiplier up to +/- 1 part per 1.
63062306a36Sopenharmony_ci	 * Here expressed as NORMAL_PPB (1 billion parts per billion).
63162306a36Sopenharmony_ci	 */
63262306a36Sopenharmony_ci	phc->ptp_info.max_adj = NORMAL_PPB;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	lif->phc = phc;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_civoid ionic_lif_free_phc(struct ionic_lif *lif)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	if (!lif->phc)
64062306a36Sopenharmony_ci		return;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	mutex_destroy(&lif->phc->config_lock);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	devm_kfree(lif->ionic->dev, lif->phc);
64562306a36Sopenharmony_ci	lif->phc = NULL;
64662306a36Sopenharmony_ci}
647