162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ptp_classify.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "lan966x_main.h"
662306a36Sopenharmony_ci#include "vcap_api.h"
762306a36Sopenharmony_ci#include "vcap_api_client.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define LAN966X_MAX_PTP_ID	512
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* Represents 1ppm adjustment in 2^59 format with 6.037735849ns as reference
1262306a36Sopenharmony_ci * The value is calculated as following: (1/1000000)/((2^-59)/6.037735849)
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#define LAN966X_1PPM_FORMAT		3480517749723LL
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Represents 1ppb adjustment in 2^29 format with 6.037735849ns as reference
1762306a36Sopenharmony_ci * The value is calculated as following: (1/1000000000)/((2^59)/6.037735849)
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci#define LAN966X_1PPB_FORMAT		3480517749LL
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define TOD_ACC_PIN		0x7
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* This represents the base rule ID for the PTP rules that are added in the
2462306a36Sopenharmony_ci * VCAP to trap frames to CPU. This number needs to be bigger than the maximum
2562306a36Sopenharmony_ci * number of entries that can exist in the VCAP.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#define LAN966X_VCAP_PTP_RULE_ID	1000000
2862306a36Sopenharmony_ci#define LAN966X_VCAP_L2_PTP_TRAP	(LAN966X_VCAP_PTP_RULE_ID + 0)
2962306a36Sopenharmony_ci#define LAN966X_VCAP_IPV4_EV_PTP_TRAP	(LAN966X_VCAP_PTP_RULE_ID + 1)
3062306a36Sopenharmony_ci#define LAN966X_VCAP_IPV4_GEN_PTP_TRAP	(LAN966X_VCAP_PTP_RULE_ID + 2)
3162306a36Sopenharmony_ci#define LAN966X_VCAP_IPV6_EV_PTP_TRAP	(LAN966X_VCAP_PTP_RULE_ID + 3)
3262306a36Sopenharmony_ci#define LAN966X_VCAP_IPV6_GEN_PTP_TRAP	(LAN966X_VCAP_PTP_RULE_ID + 4)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cienum {
3562306a36Sopenharmony_ci	PTP_PIN_ACTION_IDLE = 0,
3662306a36Sopenharmony_ci	PTP_PIN_ACTION_LOAD,
3762306a36Sopenharmony_ci	PTP_PIN_ACTION_SAVE,
3862306a36Sopenharmony_ci	PTP_PIN_ACTION_CLOCK,
3962306a36Sopenharmony_ci	PTP_PIN_ACTION_DELTA,
4062306a36Sopenharmony_ci	PTP_PIN_ACTION_TOD
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic u64 lan966x_ptp_get_nominal_value(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	/* This is the default value that for each system clock, the time of day
4662306a36Sopenharmony_ci	 * is increased. It has the format 5.59 nanosecond.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	return 0x304d4873ecade305;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int lan966x_ptp_add_trap(struct lan966x_port *port,
5262306a36Sopenharmony_ci				int (*add_ptp_key)(struct vcap_rule *vrule,
5362306a36Sopenharmony_ci						   struct lan966x_port*),
5462306a36Sopenharmony_ci				u32 rule_id,
5562306a36Sopenharmony_ci				u16 proto)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
5862306a36Sopenharmony_ci	struct vcap_rule *vrule;
5962306a36Sopenharmony_ci	int err;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id);
6262306a36Sopenharmony_ci	if (!IS_ERR(vrule)) {
6362306a36Sopenharmony_ci		u32 value, mask;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		/* Just modify the ingress port mask and exit */
6662306a36Sopenharmony_ci		vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK,
6762306a36Sopenharmony_ci				      &value, &mask);
6862306a36Sopenharmony_ci		mask &= ~BIT(port->chip_port);
6962306a36Sopenharmony_ci		vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK,
7062306a36Sopenharmony_ci				      value, mask);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		err = vcap_mod_rule(vrule);
7362306a36Sopenharmony_ci		goto free_rule;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	vrule = vcap_alloc_rule(lan966x->vcap_ctrl, port->dev,
7762306a36Sopenharmony_ci				LAN966X_VCAP_CID_IS2_L0,
7862306a36Sopenharmony_ci				VCAP_USER_PTP, 0, rule_id);
7962306a36Sopenharmony_ci	if (IS_ERR(vrule))
8062306a36Sopenharmony_ci		return PTR_ERR(vrule);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	err = add_ptp_key(vrule, port);
8362306a36Sopenharmony_ci	if (err)
8462306a36Sopenharmony_ci		goto free_rule;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	err = vcap_rule_add_action_bit(vrule, VCAP_AF_CPU_COPY_ENA, VCAP_BIT_1);
8762306a36Sopenharmony_ci	err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, LAN966X_PMM_REPLACE);
8862306a36Sopenharmony_ci	err |= vcap_val_rule(vrule, proto);
8962306a36Sopenharmony_ci	if (err)
9062306a36Sopenharmony_ci		goto free_rule;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	err = vcap_add_rule(vrule);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cifree_rule:
9562306a36Sopenharmony_ci	/* Free the local copy of the rule */
9662306a36Sopenharmony_ci	vcap_free_rule(vrule);
9762306a36Sopenharmony_ci	return err;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int lan966x_ptp_del_trap(struct lan966x_port *port,
10162306a36Sopenharmony_ci				u32 rule_id)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
10462306a36Sopenharmony_ci	struct vcap_rule *vrule;
10562306a36Sopenharmony_ci	u32 value, mask;
10662306a36Sopenharmony_ci	int err;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id);
10962306a36Sopenharmony_ci	if (IS_ERR(vrule))
11062306a36Sopenharmony_ci		return -EEXIST;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask);
11362306a36Sopenharmony_ci	mask |= BIT(port->chip_port);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* No other port requires this trap, so it is safe to remove it */
11662306a36Sopenharmony_ci	if (mask == GENMASK(lan966x->num_phys_ports, 0)) {
11762306a36Sopenharmony_ci		err = vcap_del_rule(lan966x->vcap_ctrl, port->dev, rule_id);
11862306a36Sopenharmony_ci		goto free_rule;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, value, mask);
12262306a36Sopenharmony_ci	err = vcap_mod_rule(vrule);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cifree_rule:
12562306a36Sopenharmony_ci	vcap_free_rule(vrule);
12662306a36Sopenharmony_ci	return err;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int lan966x_ptp_add_l2_key(struct vcap_rule *vrule,
13062306a36Sopenharmony_ci				  struct lan966x_port *port)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return vcap_rule_add_key_u32(vrule, VCAP_KF_ETYPE, ETH_P_1588, ~0);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int lan966x_ptp_add_ip_event_key(struct vcap_rule *vrule,
13662306a36Sopenharmony_ci					struct lan966x_port *port)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_EV_PORT, ~0) ||
13962306a36Sopenharmony_ci	       vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int lan966x_ptp_add_ip_general_key(struct vcap_rule *vrule,
14362306a36Sopenharmony_ci					  struct lan966x_port *port)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_GEN_PORT, ~0) ||
14662306a36Sopenharmony_ci	       vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int lan966x_ptp_add_l2_rule(struct lan966x_port *port)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	return lan966x_ptp_add_trap(port, lan966x_ptp_add_l2_key,
15262306a36Sopenharmony_ci				    LAN966X_VCAP_L2_PTP_TRAP, ETH_P_ALL);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int lan966x_ptp_add_ipv4_rules(struct lan966x_port *port)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key,
16062306a36Sopenharmony_ci				   LAN966X_VCAP_IPV4_EV_PTP_TRAP, ETH_P_IP);
16162306a36Sopenharmony_ci	if (err)
16262306a36Sopenharmony_ci		return err;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key,
16562306a36Sopenharmony_ci				   LAN966X_VCAP_IPV4_GEN_PTP_TRAP, ETH_P_IP);
16662306a36Sopenharmony_ci	if (err)
16762306a36Sopenharmony_ci		lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return err;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int lan966x_ptp_add_ipv6_rules(struct lan966x_port *port)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	int err;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key,
17762306a36Sopenharmony_ci				   LAN966X_VCAP_IPV6_EV_PTP_TRAP, ETH_P_IPV6);
17862306a36Sopenharmony_ci	if (err)
17962306a36Sopenharmony_ci		return err;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key,
18262306a36Sopenharmony_ci				   LAN966X_VCAP_IPV6_GEN_PTP_TRAP, ETH_P_IPV6);
18362306a36Sopenharmony_ci	if (err)
18462306a36Sopenharmony_ci		lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return err;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int lan966x_ptp_del_l2_rule(struct lan966x_port *port)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	return lan966x_ptp_del_trap(port, LAN966X_VCAP_L2_PTP_TRAP);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int lan966x_ptp_del_ipv4_rules(struct lan966x_port *port)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	int err;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP);
19962306a36Sopenharmony_ci	err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_GEN_PTP_TRAP);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return err;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int lan966x_ptp_del_ipv6_rules(struct lan966x_port *port)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	int err;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP);
20962306a36Sopenharmony_ci	err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_GEN_PTP_TRAP);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return err;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int lan966x_ptp_add_traps(struct lan966x_port *port)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	int err;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	err = lan966x_ptp_add_l2_rule(port);
21962306a36Sopenharmony_ci	if (err)
22062306a36Sopenharmony_ci		goto err_l2;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	err = lan966x_ptp_add_ipv4_rules(port);
22362306a36Sopenharmony_ci	if (err)
22462306a36Sopenharmony_ci		goto err_ipv4;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	err = lan966x_ptp_add_ipv6_rules(port);
22762306a36Sopenharmony_ci	if (err)
22862306a36Sopenharmony_ci		goto err_ipv6;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return err;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cierr_ipv6:
23362306a36Sopenharmony_ci	lan966x_ptp_del_ipv4_rules(port);
23462306a36Sopenharmony_cierr_ipv4:
23562306a36Sopenharmony_ci	lan966x_ptp_del_l2_rule(port);
23662306a36Sopenharmony_cierr_l2:
23762306a36Sopenharmony_ci	return err;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciint lan966x_ptp_del_traps(struct lan966x_port *port)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	int err;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	err = lan966x_ptp_del_l2_rule(port);
24562306a36Sopenharmony_ci	err |= lan966x_ptp_del_ipv4_rules(port);
24662306a36Sopenharmony_ci	err |= lan966x_ptp_del_ipv6_rules(port);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return err;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ciint lan966x_ptp_setup_traps(struct lan966x_port *port,
25262306a36Sopenharmony_ci			    struct kernel_hwtstamp_config *cfg)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	if (cfg->rx_filter == HWTSTAMP_FILTER_NONE)
25562306a36Sopenharmony_ci		return lan966x_ptp_del_traps(port);
25662306a36Sopenharmony_ci	else
25762306a36Sopenharmony_ci		return lan966x_ptp_add_traps(port);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciint lan966x_ptp_hwtstamp_set(struct lan966x_port *port,
26162306a36Sopenharmony_ci			     struct kernel_hwtstamp_config *cfg,
26262306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
26562306a36Sopenharmony_ci	struct lan966x_phc *phc;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	switch (cfg->tx_type) {
26862306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
26962306a36Sopenharmony_ci		port->ptp_tx_cmd = IFH_REW_OP_TWO_STEP_PTP;
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	case HWTSTAMP_TX_ONESTEP_SYNC:
27262306a36Sopenharmony_ci		port->ptp_tx_cmd = IFH_REW_OP_ONE_STEP_PTP;
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
27562306a36Sopenharmony_ci		port->ptp_tx_cmd = IFH_REW_OP_NOOP;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	default:
27862306a36Sopenharmony_ci		return -ERANGE;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	switch (cfg->rx_filter) {
28262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
28362306a36Sopenharmony_ci		port->ptp_rx_cmd = false;
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
28662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
28762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
28862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
28962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
29062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
29162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
29262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
29362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
29462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
29562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
29662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
29762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
29862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
29962306a36Sopenharmony_ci		port->ptp_rx_cmd = true;
30062306a36Sopenharmony_ci		cfg->rx_filter = HWTSTAMP_FILTER_ALL;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	default:
30362306a36Sopenharmony_ci		return -ERANGE;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* Commit back the result & save it */
30762306a36Sopenharmony_ci	mutex_lock(&lan966x->ptp_lock);
30862306a36Sopenharmony_ci	phc = &lan966x->phc[LAN966X_PHC_PORT];
30962306a36Sopenharmony_ci	phc->hwtstamp_config = *cfg;
31062306a36Sopenharmony_ci	mutex_unlock(&lan966x->ptp_lock);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_civoid lan966x_ptp_hwtstamp_get(struct lan966x_port *port,
31662306a36Sopenharmony_ci			      struct kernel_hwtstamp_config *cfg)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
31962306a36Sopenharmony_ci	struct lan966x_phc *phc;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	phc = &lan966x->phc[LAN966X_PHC_PORT];
32262306a36Sopenharmony_ci	*cfg = phc->hwtstamp_config;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct ptp_header *header;
32862306a36Sopenharmony_ci	u8 msgtype;
32962306a36Sopenharmony_ci	int type;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (port->ptp_tx_cmd == IFH_REW_OP_NOOP)
33262306a36Sopenharmony_ci		return IFH_REW_OP_NOOP;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	type = ptp_classify_raw(skb);
33562306a36Sopenharmony_ci	if (type == PTP_CLASS_NONE)
33662306a36Sopenharmony_ci		return IFH_REW_OP_NOOP;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	header = ptp_parse_header(skb, type);
33962306a36Sopenharmony_ci	if (!header)
34062306a36Sopenharmony_ci		return IFH_REW_OP_NOOP;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (port->ptp_tx_cmd == IFH_REW_OP_TWO_STEP_PTP)
34362306a36Sopenharmony_ci		return IFH_REW_OP_TWO_STEP_PTP;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* If it is sync and run 1 step then set the correct operation,
34662306a36Sopenharmony_ci	 * otherwise run as 2 step
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	msgtype = ptp_get_msgtype(header, type);
34962306a36Sopenharmony_ci	if ((msgtype & 0xf) == 0)
35062306a36Sopenharmony_ci		return IFH_REW_OP_ONE_STEP_PTP;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return IFH_REW_OP_TWO_STEP_PTP;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void lan966x_ptp_txtstamp_old_release(struct lan966x_port *port)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct sk_buff *skb, *skb_tmp;
35862306a36Sopenharmony_ci	unsigned long flags;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	spin_lock_irqsave(&port->tx_skbs.lock, flags);
36162306a36Sopenharmony_ci	skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
36262306a36Sopenharmony_ci		if time_after(LAN966X_SKB_CB(skb)->jiffies + LAN966X_PTP_TIMEOUT,
36362306a36Sopenharmony_ci			      jiffies)
36462306a36Sopenharmony_ci			break;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		__skb_unlink(skb, &port->tx_skbs);
36762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ciint lan966x_ptp_txtstamp_request(struct lan966x_port *port,
37362306a36Sopenharmony_ci				 struct sk_buff *skb)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
37662306a36Sopenharmony_ci	unsigned long flags;
37762306a36Sopenharmony_ci	u8 rew_op;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	rew_op = lan966x_ptp_classify(port, skb);
38062306a36Sopenharmony_ci	LAN966X_SKB_CB(skb)->rew_op = rew_op;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (rew_op != IFH_REW_OP_TWO_STEP_PTP)
38362306a36Sopenharmony_ci		return 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	lan966x_ptp_txtstamp_old_release(port);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags);
38862306a36Sopenharmony_ci	if (lan966x->ptp_skbs == LAN966X_MAX_PTP_ID) {
38962306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags);
39062306a36Sopenharmony_ci		return -EBUSY;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	skb_queue_tail(&port->tx_skbs, skb);
39662306a36Sopenharmony_ci	LAN966X_SKB_CB(skb)->ts_id = port->ts_id;
39762306a36Sopenharmony_ci	LAN966X_SKB_CB(skb)->jiffies = jiffies;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	lan966x->ptp_skbs++;
40062306a36Sopenharmony_ci	port->ts_id++;
40162306a36Sopenharmony_ci	if (port->ts_id == LAN966X_MAX_PTP_ID)
40262306a36Sopenharmony_ci		port->ts_id = 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_civoid lan966x_ptp_txtstamp_release(struct lan966x_port *port,
41062306a36Sopenharmony_ci				  struct sk_buff *skb)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
41362306a36Sopenharmony_ci	unsigned long flags;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags);
41662306a36Sopenharmony_ci	port->ts_id--;
41762306a36Sopenharmony_ci	lan966x->ptp_skbs--;
41862306a36Sopenharmony_ci	skb_unlink(skb, &port->tx_skbs);
41962306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void lan966x_get_hwtimestamp(struct lan966x *lan966x,
42362306a36Sopenharmony_ci				    struct timespec64 *ts,
42462306a36Sopenharmony_ci				    u32 nsec)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	/* Read current PTP time to get seconds */
42762306a36Sopenharmony_ci	unsigned long flags;
42862306a36Sopenharmony_ci	u32 curr_nsec;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) |
43362306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(LAN966X_PHC_PORT) |
43462306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(0),
43562306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
43662306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
43762306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC,
43862306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	ts->tv_sec = lan_rd(lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN));
44162306a36Sopenharmony_ci	curr_nsec = lan_rd(lan966x, PTP_TOD_NSEC(TOD_ACC_PIN));
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ts->tv_nsec = nsec;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Sec has incremented since the ts was registered */
44662306a36Sopenharmony_ci	if (curr_nsec < nsec)
44762306a36Sopenharmony_ci		ts->tv_sec--;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciirqreturn_t lan966x_ptp_irq_handler(int irq, void *args)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	int budget = LAN966X_MAX_PTP_ID;
45562306a36Sopenharmony_ci	struct lan966x *lan966x = args;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	while (budget--) {
45862306a36Sopenharmony_ci		struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
45962306a36Sopenharmony_ci		struct skb_shared_hwtstamps shhwtstamps;
46062306a36Sopenharmony_ci		struct lan966x_port *port;
46162306a36Sopenharmony_ci		struct timespec64 ts;
46262306a36Sopenharmony_ci		unsigned long flags;
46362306a36Sopenharmony_ci		u32 val, id, txport;
46462306a36Sopenharmony_ci		u32 delay;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		val = lan_rd(lan966x, PTP_TWOSTEP_CTRL);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		/* Check if a timestamp can be retrieved */
46962306a36Sopenharmony_ci		if (!(val & PTP_TWOSTEP_CTRL_VLD))
47062306a36Sopenharmony_ci			break;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		WARN_ON(val & PTP_TWOSTEP_CTRL_OVFL);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (!(val & PTP_TWOSTEP_CTRL_STAMP_TX))
47562306a36Sopenharmony_ci			continue;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		/* Retrieve the ts Tx port */
47862306a36Sopenharmony_ci		txport = PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		/* Retrieve its associated skb */
48162306a36Sopenharmony_ci		port = lan966x->ports[txport];
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		/* Retrieve the delay */
48462306a36Sopenharmony_ci		delay = lan_rd(lan966x, PTP_TWOSTEP_STAMP);
48562306a36Sopenharmony_ci		delay = PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(delay);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		/* Get next timestamp from fifo, which needs to be the
48862306a36Sopenharmony_ci		 * rx timestamp which represents the id of the frame
48962306a36Sopenharmony_ci		 */
49062306a36Sopenharmony_ci		lan_rmw(PTP_TWOSTEP_CTRL_NXT_SET(1),
49162306a36Sopenharmony_ci			PTP_TWOSTEP_CTRL_NXT,
49262306a36Sopenharmony_ci			lan966x, PTP_TWOSTEP_CTRL);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		val = lan_rd(lan966x, PTP_TWOSTEP_CTRL);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		/* Check if a timestamp can be retried */
49762306a36Sopenharmony_ci		if (!(val & PTP_TWOSTEP_CTRL_VLD))
49862306a36Sopenharmony_ci			break;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		/* Read RX timestamping to get the ID */
50162306a36Sopenharmony_ci		id = lan_rd(lan966x, PTP_TWOSTEP_STAMP);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		spin_lock_irqsave(&port->tx_skbs.lock, flags);
50462306a36Sopenharmony_ci		skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
50562306a36Sopenharmony_ci			if (LAN966X_SKB_CB(skb)->ts_id != id)
50662306a36Sopenharmony_ci				continue;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci			__skb_unlink(skb, &port->tx_skbs);
50962306a36Sopenharmony_ci			skb_match = skb;
51062306a36Sopenharmony_ci			break;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci		spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		/* Next ts */
51562306a36Sopenharmony_ci		lan_rmw(PTP_TWOSTEP_CTRL_NXT_SET(1),
51662306a36Sopenharmony_ci			PTP_TWOSTEP_CTRL_NXT,
51762306a36Sopenharmony_ci			lan966x, PTP_TWOSTEP_CTRL);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (WARN_ON(!skb_match))
52062306a36Sopenharmony_ci			continue;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags);
52362306a36Sopenharmony_ci		lan966x->ptp_skbs--;
52462306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		/* Get the h/w timestamp */
52762306a36Sopenharmony_ci		lan966x_get_hwtimestamp(lan966x, &ts, delay);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		/* Set the timestamp into the skb */
53062306a36Sopenharmony_ci		shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
53162306a36Sopenharmony_ci		skb_tstamp_tx(skb_match, &shhwtstamps);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		dev_kfree_skb_any(skb_match);
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return IRQ_HANDLED;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciirqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct lan966x *lan966x = args;
54262306a36Sopenharmony_ci	struct lan966x_phc *phc;
54362306a36Sopenharmony_ci	unsigned long flags;
54462306a36Sopenharmony_ci	u64 time = 0;
54562306a36Sopenharmony_ci	time64_t s;
54662306a36Sopenharmony_ci	int pin, i;
54762306a36Sopenharmony_ci	s64 ns;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (!(lan_rd(lan966x, PTP_PIN_INTR)))
55062306a36Sopenharmony_ci		return IRQ_NONE;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Go through all domains and see which pin generated the interrupt */
55362306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
55462306a36Sopenharmony_ci		struct ptp_clock_event ptp_event = {0};
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		phc = &lan966x->phc[i];
55762306a36Sopenharmony_ci		pin = ptp_find_pin_unlocked(phc->clock, PTP_PF_EXTTS, 0);
55862306a36Sopenharmony_ci		if (pin == -1)
55962306a36Sopenharmony_ci			continue;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		if (!(lan_rd(lan966x, PTP_PIN_INTR) & BIT(pin)))
56262306a36Sopenharmony_ci			continue;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		/* Enable to get the new interrupt.
56762306a36Sopenharmony_ci		 * By writing 1 it clears the bit
56862306a36Sopenharmony_ci		 */
56962306a36Sopenharmony_ci		lan_wr(BIT(pin), lan966x, PTP_PIN_INTR);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		/* Get current time */
57262306a36Sopenharmony_ci		s = lan_rd(lan966x, PTP_TOD_SEC_MSB(pin));
57362306a36Sopenharmony_ci		s <<= 32;
57462306a36Sopenharmony_ci		s |= lan_rd(lan966x, PTP_TOD_SEC_LSB(pin));
57562306a36Sopenharmony_ci		ns = lan_rd(lan966x, PTP_TOD_NSEC(pin));
57662306a36Sopenharmony_ci		ns &= PTP_TOD_NSEC_TOD_NSEC;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) {
58162306a36Sopenharmony_ci			s--;
58262306a36Sopenharmony_ci			ns &= 0xf;
58362306a36Sopenharmony_ci			ns += 999999984;
58462306a36Sopenharmony_ci		}
58562306a36Sopenharmony_ci		time = ktime_set(s, ns);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		ptp_event.index = pin;
58862306a36Sopenharmony_ci		ptp_event.timestamp = time;
58962306a36Sopenharmony_ci		ptp_event.type = PTP_CLOCK_EXTTS;
59062306a36Sopenharmony_ci		ptp_clock_event(phc->clock, &ptp_event);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return IRQ_HANDLED;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int lan966x_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
59962306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
60062306a36Sopenharmony_ci	unsigned long flags;
60162306a36Sopenharmony_ci	bool neg_adj = 0;
60262306a36Sopenharmony_ci	u64 tod_inc;
60362306a36Sopenharmony_ci	u64 ref;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (!scaled_ppm)
60662306a36Sopenharmony_ci		return 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (scaled_ppm < 0) {
60962306a36Sopenharmony_ci		neg_adj = 1;
61062306a36Sopenharmony_ci		scaled_ppm = -scaled_ppm;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	tod_inc = lan966x_ptp_get_nominal_value();
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* The multiplication is split in 2 separate additions because of
61662306a36Sopenharmony_ci	 * overflow issues. If scaled_ppm with 16bit fractional part was bigger
61762306a36Sopenharmony_ci	 * than 20ppm then we got overflow.
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	ref = LAN966X_1PPM_FORMAT * (scaled_ppm >> 16);
62062306a36Sopenharmony_ci	ref += (LAN966X_1PPM_FORMAT * (0xffff & scaled_ppm)) >> 16;
62162306a36Sopenharmony_ci	tod_inc = neg_adj ? tod_inc - ref : tod_inc + ref;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(1 << BIT(phc->index)),
62662306a36Sopenharmony_ci		PTP_DOM_CFG_CLKCFG_DIS,
62762306a36Sopenharmony_ci		lan966x, PTP_DOM_CFG);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	lan_wr((u32)tod_inc & 0xFFFFFFFF, lan966x,
63062306a36Sopenharmony_ci	       PTP_CLK_PER_CFG(phc->index, 0));
63162306a36Sopenharmony_ci	lan_wr((u32)(tod_inc >> 32), lan966x,
63262306a36Sopenharmony_ci	       PTP_CLK_PER_CFG(phc->index, 1));
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0),
63562306a36Sopenharmony_ci		PTP_DOM_CFG_CLKCFG_DIS,
63662306a36Sopenharmony_ci		lan966x, PTP_DOM_CFG);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int lan966x_ptp_settime64(struct ptp_clock_info *ptp,
64462306a36Sopenharmony_ci				 const struct timespec64 *ts)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
64762306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
64862306a36Sopenharmony_ci	unsigned long flags;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* Must be in IDLE mode before the time can be loaded */
65362306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) |
65462306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
65562306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(0),
65662306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
65762306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
65862306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC,
65962306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Set new value */
66262306a36Sopenharmony_ci	lan_wr(PTP_TOD_SEC_MSB_TOD_SEC_MSB_SET(upper_32_bits(ts->tv_sec)),
66362306a36Sopenharmony_ci	       lan966x, PTP_TOD_SEC_MSB(TOD_ACC_PIN));
66462306a36Sopenharmony_ci	lan_wr(lower_32_bits(ts->tv_sec),
66562306a36Sopenharmony_ci	       lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN));
66662306a36Sopenharmony_ci	lan_wr(ts->tv_nsec, lan966x, PTP_TOD_NSEC(TOD_ACC_PIN));
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Apply new values */
66962306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_LOAD) |
67062306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
67162306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(0),
67262306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
67362306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
67462306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC,
67562306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ciint lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
68562306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
68662306a36Sopenharmony_ci	unsigned long flags;
68762306a36Sopenharmony_ci	time64_t s;
68862306a36Sopenharmony_ci	s64 ns;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) |
69362306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
69462306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(0),
69562306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
69662306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
69762306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC,
69862306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	s = lan_rd(lan966x, PTP_TOD_SEC_MSB(TOD_ACC_PIN));
70162306a36Sopenharmony_ci	s <<= 32;
70262306a36Sopenharmony_ci	s |= lan_rd(lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN));
70362306a36Sopenharmony_ci	ns = lan_rd(lan966x, PTP_TOD_NSEC(TOD_ACC_PIN));
70462306a36Sopenharmony_ci	ns &= PTP_TOD_NSEC_TOD_NSEC;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	/* Deal with negative values */
70962306a36Sopenharmony_ci	if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) {
71062306a36Sopenharmony_ci		s--;
71162306a36Sopenharmony_ci		ns &= 0xf;
71262306a36Sopenharmony_ci		ns += 999999984;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	set_normalized_timespec64(ts, s, ns);
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int lan966x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
72262306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
72562306a36Sopenharmony_ci		unsigned long flags;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		/* Must be in IDLE mode before the time can be loaded */
73062306a36Sopenharmony_ci		lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) |
73162306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
73262306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC_SET(0),
73362306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_ACTION |
73462306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM |
73562306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC,
73662306a36Sopenharmony_ci			lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		lan_wr(PTP_TOD_NSEC_TOD_NSEC_SET(delta),
73962306a36Sopenharmony_ci		       lan966x, PTP_TOD_NSEC(TOD_ACC_PIN));
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		/* Adjust time with the value of PTP_TOD_NSEC */
74262306a36Sopenharmony_ci		lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_DELTA) |
74362306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
74462306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC_SET(0),
74562306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_ACTION |
74662306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM |
74762306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC,
74862306a36Sopenharmony_ci			lan966x, PTP_PIN_CFG(TOD_ACC_PIN));
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
75162306a36Sopenharmony_ci	} else {
75262306a36Sopenharmony_ci		/* Fall back using lan966x_ptp_settime64 which is not exact */
75362306a36Sopenharmony_ci		struct timespec64 ts;
75462306a36Sopenharmony_ci		u64 now;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		lan966x_ptp_gettime64(ptp, &ts);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		now = ktime_to_ns(timespec64_to_ktime(ts));
75962306a36Sopenharmony_ci		ts = ns_to_timespec64(now + delta);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		lan966x_ptp_settime64(ptp, &ts);
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	return 0;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int lan966x_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
76862306a36Sopenharmony_ci			      enum ptp_pin_function func, unsigned int chan)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
77162306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
77262306a36Sopenharmony_ci	struct ptp_clock_info *info;
77362306a36Sopenharmony_ci	int i;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* Currently support only 1 channel */
77662306a36Sopenharmony_ci	if (chan != 0)
77762306a36Sopenharmony_ci		return -1;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	switch (func) {
78062306a36Sopenharmony_ci	case PTP_PF_NONE:
78162306a36Sopenharmony_ci	case PTP_PF_PEROUT:
78262306a36Sopenharmony_ci	case PTP_PF_EXTTS:
78362306a36Sopenharmony_ci		break;
78462306a36Sopenharmony_ci	default:
78562306a36Sopenharmony_ci		return -1;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* The PTP pins are shared by all the PHC. So it is required to see if
78962306a36Sopenharmony_ci	 * the pin is connected to another PHC. The pin is connected to another
79062306a36Sopenharmony_ci	 * PHC if that pin already has a function on that PHC.
79162306a36Sopenharmony_ci	 */
79262306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
79362306a36Sopenharmony_ci		info = &lan966x->phc[i].info;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		/* Ignore the check with ourself */
79662306a36Sopenharmony_ci		if (ptp == info)
79762306a36Sopenharmony_ci			continue;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		if (info->pin_config[pin].func == PTP_PF_PEROUT ||
80062306a36Sopenharmony_ci		    info->pin_config[pin].func == PTP_PF_EXTTS)
80162306a36Sopenharmony_ci			return -1;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic int lan966x_ptp_perout(struct ptp_clock_info *ptp,
80862306a36Sopenharmony_ci			      struct ptp_clock_request *rq, int on)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
81162306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
81262306a36Sopenharmony_ci	struct timespec64 ts_phase, ts_period;
81362306a36Sopenharmony_ci	unsigned long flags;
81462306a36Sopenharmony_ci	s64 wf_high, wf_low;
81562306a36Sopenharmony_ci	bool pps = false;
81662306a36Sopenharmony_ci	int pin;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
81962306a36Sopenharmony_ci				 PTP_PEROUT_PHASE))
82062306a36Sopenharmony_ci		return -EOPNOTSUPP;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	pin = ptp_find_pin(phc->clock, PTP_PF_PEROUT, rq->perout.index);
82362306a36Sopenharmony_ci	if (pin == -1 || pin >= LAN966X_PHC_PINS_NUM)
82462306a36Sopenharmony_ci		return -EINVAL;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!on) {
82762306a36Sopenharmony_ci		spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
82862306a36Sopenharmony_ci		lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) |
82962306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
83062306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC_SET(0),
83162306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_ACTION |
83262306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM |
83362306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC,
83462306a36Sopenharmony_ci			lan966x, PTP_PIN_CFG(pin));
83562306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
83662306a36Sopenharmony_ci		return 0;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (rq->perout.period.sec == 1 &&
84062306a36Sopenharmony_ci	    rq->perout.period.nsec == 0)
84162306a36Sopenharmony_ci		pps = true;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (rq->perout.flags & PTP_PEROUT_PHASE) {
84462306a36Sopenharmony_ci		ts_phase.tv_sec = rq->perout.phase.sec;
84562306a36Sopenharmony_ci		ts_phase.tv_nsec = rq->perout.phase.nsec;
84662306a36Sopenharmony_ci	} else {
84762306a36Sopenharmony_ci		ts_phase.tv_sec = rq->perout.start.sec;
84862306a36Sopenharmony_ci		ts_phase.tv_nsec = rq->perout.start.nsec;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (ts_phase.tv_sec || (ts_phase.tv_nsec && !pps)) {
85262306a36Sopenharmony_ci		dev_warn(lan966x->dev,
85362306a36Sopenharmony_ci			 "Absolute time not supported!\n");
85462306a36Sopenharmony_ci		return -EINVAL;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
85862306a36Sopenharmony_ci		struct timespec64 ts_on;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		ts_on.tv_sec = rq->perout.on.sec;
86162306a36Sopenharmony_ci		ts_on.tv_nsec = rq->perout.on.nsec;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		wf_high = timespec64_to_ns(&ts_on);
86462306a36Sopenharmony_ci	} else {
86562306a36Sopenharmony_ci		wf_high = 5000;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (pps) {
86962306a36Sopenharmony_ci		spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
87062306a36Sopenharmony_ci		lan_wr(PTP_WF_LOW_PERIOD_PIN_WFL(ts_phase.tv_nsec),
87162306a36Sopenharmony_ci		       lan966x, PTP_WF_LOW_PERIOD(pin));
87262306a36Sopenharmony_ci		lan_wr(PTP_WF_HIGH_PERIOD_PIN_WFH(wf_high),
87362306a36Sopenharmony_ci		       lan966x, PTP_WF_HIGH_PERIOD(pin));
87462306a36Sopenharmony_ci		lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_CLOCK) |
87562306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
87662306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC_SET(3),
87762306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_ACTION |
87862306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_DOM |
87962306a36Sopenharmony_ci			PTP_PIN_CFG_PIN_SYNC,
88062306a36Sopenharmony_ci			lan966x, PTP_PIN_CFG(pin));
88162306a36Sopenharmony_ci		spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
88262306a36Sopenharmony_ci		return 0;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	ts_period.tv_sec = rq->perout.period.sec;
88662306a36Sopenharmony_ci	ts_period.tv_nsec = rq->perout.period.nsec;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	wf_low = timespec64_to_ns(&ts_period);
88962306a36Sopenharmony_ci	wf_low -= wf_high;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
89262306a36Sopenharmony_ci	lan_wr(PTP_WF_LOW_PERIOD_PIN_WFL(wf_low),
89362306a36Sopenharmony_ci	       lan966x, PTP_WF_LOW_PERIOD(pin));
89462306a36Sopenharmony_ci	lan_wr(PTP_WF_HIGH_PERIOD_PIN_WFH(wf_high),
89562306a36Sopenharmony_ci	       lan966x, PTP_WF_HIGH_PERIOD(pin));
89662306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_CLOCK) |
89762306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
89862306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(0),
89962306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
90062306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
90162306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC,
90262306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(pin));
90362306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return 0;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int lan966x_ptp_extts(struct ptp_clock_info *ptp,
90962306a36Sopenharmony_ci			     struct ptp_clock_request *rq, int on)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
91262306a36Sopenharmony_ci	struct lan966x *lan966x = phc->lan966x;
91362306a36Sopenharmony_ci	unsigned long flags;
91462306a36Sopenharmony_ci	int pin;
91562306a36Sopenharmony_ci	u32 val;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (lan966x->ptp_ext_irq <= 0)
91862306a36Sopenharmony_ci		return -EOPNOTSUPP;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* Reject requests with unsupported flags */
92162306a36Sopenharmony_ci	if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
92262306a36Sopenharmony_ci				PTP_RISING_EDGE |
92362306a36Sopenharmony_ci				PTP_STRICT_FLAGS))
92462306a36Sopenharmony_ci		return -EOPNOTSUPP;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	pin = ptp_find_pin(phc->clock, PTP_PF_EXTTS, rq->extts.index);
92762306a36Sopenharmony_ci	if (pin == -1 || pin >= LAN966X_PHC_PINS_NUM)
92862306a36Sopenharmony_ci		return -EINVAL;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
93162306a36Sopenharmony_ci	lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) |
93262306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC_SET(on ? 3 : 0) |
93362306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
93462306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SELECT_SET(pin),
93562306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_ACTION |
93662306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SYNC |
93762306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_DOM |
93862306a36Sopenharmony_ci		PTP_PIN_CFG_PIN_SELECT,
93962306a36Sopenharmony_ci		lan966x, PTP_PIN_CFG(pin));
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	val = lan_rd(lan966x, PTP_PIN_INTR_ENA);
94262306a36Sopenharmony_ci	if (on)
94362306a36Sopenharmony_ci		val |= BIT(pin);
94462306a36Sopenharmony_ci	else
94562306a36Sopenharmony_ci		val &= ~BIT(pin);
94662306a36Sopenharmony_ci	lan_wr(val, lan966x, PTP_PIN_INTR_ENA);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	return 0;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic int lan966x_ptp_enable(struct ptp_clock_info *ptp,
95462306a36Sopenharmony_ci			      struct ptp_clock_request *rq, int on)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	switch (rq->type) {
95762306a36Sopenharmony_ci	case PTP_CLK_REQ_PEROUT:
95862306a36Sopenharmony_ci		return lan966x_ptp_perout(ptp, rq, on);
95962306a36Sopenharmony_ci	case PTP_CLK_REQ_EXTTS:
96062306a36Sopenharmony_ci		return lan966x_ptp_extts(ptp, rq, on);
96162306a36Sopenharmony_ci	default:
96262306a36Sopenharmony_ci		return -EOPNOTSUPP;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return 0;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic struct ptp_clock_info lan966x_ptp_clock_info = {
96962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
97062306a36Sopenharmony_ci	.name		= "lan966x ptp",
97162306a36Sopenharmony_ci	.max_adj	= 200000,
97262306a36Sopenharmony_ci	.gettime64	= lan966x_ptp_gettime64,
97362306a36Sopenharmony_ci	.settime64	= lan966x_ptp_settime64,
97462306a36Sopenharmony_ci	.adjtime	= lan966x_ptp_adjtime,
97562306a36Sopenharmony_ci	.adjfine	= lan966x_ptp_adjfine,
97662306a36Sopenharmony_ci	.verify		= lan966x_ptp_verify,
97762306a36Sopenharmony_ci	.enable		= lan966x_ptp_enable,
97862306a36Sopenharmony_ci	.n_per_out	= LAN966X_PHC_PINS_NUM,
97962306a36Sopenharmony_ci	.n_ext_ts	= LAN966X_PHC_PINS_NUM,
98062306a36Sopenharmony_ci	.n_pins		= LAN966X_PHC_PINS_NUM,
98162306a36Sopenharmony_ci};
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic int lan966x_ptp_phc_init(struct lan966x *lan966x,
98462306a36Sopenharmony_ci				int index,
98562306a36Sopenharmony_ci				struct ptp_clock_info *clock_info)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct lan966x_phc *phc = &lan966x->phc[index];
98862306a36Sopenharmony_ci	struct ptp_pin_desc *p;
98962306a36Sopenharmony_ci	int i;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_PINS_NUM; i++) {
99262306a36Sopenharmony_ci		p = &phc->pins[i];
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		snprintf(p->name, sizeof(p->name), "pin%d", i);
99562306a36Sopenharmony_ci		p->index = i;
99662306a36Sopenharmony_ci		p->func = PTP_PF_NONE;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	phc->info = *clock_info;
100062306a36Sopenharmony_ci	phc->info.pin_config = &phc->pins[0];
100162306a36Sopenharmony_ci	phc->clock = ptp_clock_register(&phc->info, lan966x->dev);
100262306a36Sopenharmony_ci	if (IS_ERR(phc->clock))
100362306a36Sopenharmony_ci		return PTR_ERR(phc->clock);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	phc->index = index;
100662306a36Sopenharmony_ci	phc->lan966x = lan966x;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ciint lan966x_ptp_init(struct lan966x *lan966x)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	u64 tod_adj = lan966x_ptp_get_nominal_value();
101462306a36Sopenharmony_ci	struct lan966x_port *port;
101562306a36Sopenharmony_ci	int err, i;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (!lan966x->ptp)
101862306a36Sopenharmony_ci		return 0;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
102162306a36Sopenharmony_ci		err = lan966x_ptp_phc_init(lan966x, i, &lan966x_ptp_clock_info);
102262306a36Sopenharmony_ci		if (err)
102362306a36Sopenharmony_ci			return err;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	spin_lock_init(&lan966x->ptp_clock_lock);
102762306a36Sopenharmony_ci	spin_lock_init(&lan966x->ptp_ts_id_lock);
102862306a36Sopenharmony_ci	mutex_init(&lan966x->ptp_lock);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* Disable master counters */
103162306a36Sopenharmony_ci	lan_wr(PTP_DOM_CFG_ENA_SET(0), lan966x, PTP_DOM_CFG);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* Configure the nominal TOD increment per clock cycle */
103462306a36Sopenharmony_ci	lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0x7),
103562306a36Sopenharmony_ci		PTP_DOM_CFG_CLKCFG_DIS,
103662306a36Sopenharmony_ci		lan966x, PTP_DOM_CFG);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
103962306a36Sopenharmony_ci		lan_wr((u32)tod_adj & 0xFFFFFFFF, lan966x,
104062306a36Sopenharmony_ci		       PTP_CLK_PER_CFG(i, 0));
104162306a36Sopenharmony_ci		lan_wr((u32)(tod_adj >> 32), lan966x,
104262306a36Sopenharmony_ci		       PTP_CLK_PER_CFG(i, 1));
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0),
104662306a36Sopenharmony_ci		PTP_DOM_CFG_CLKCFG_DIS,
104762306a36Sopenharmony_ci		lan966x, PTP_DOM_CFG);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/* Enable master counters */
105062306a36Sopenharmony_ci	lan_wr(PTP_DOM_CFG_ENA_SET(0x7), lan966x, PTP_DOM_CFG);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	for (i = 0; i < lan966x->num_phys_ports; i++) {
105362306a36Sopenharmony_ci		port = lan966x->ports[i];
105462306a36Sopenharmony_ci		if (!port)
105562306a36Sopenharmony_ci			continue;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		skb_queue_head_init(&port->tx_skbs);
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return 0;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_civoid lan966x_ptp_deinit(struct lan966x *lan966x)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct lan966x_port *port;
106662306a36Sopenharmony_ci	int i;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (!lan966x->ptp)
106962306a36Sopenharmony_ci		return;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	for (i = 0; i < lan966x->num_phys_ports; i++) {
107262306a36Sopenharmony_ci		port = lan966x->ports[i];
107362306a36Sopenharmony_ci		if (!port)
107462306a36Sopenharmony_ci			continue;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		skb_queue_purge(&port->tx_skbs);
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	for (i = 0; i < LAN966X_PHC_COUNT; ++i)
108062306a36Sopenharmony_ci		ptp_clock_unregister(lan966x->phc[i].clock);
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_civoid lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb,
108462306a36Sopenharmony_ci			  u64 src_port, u64 timestamp)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct skb_shared_hwtstamps *shhwtstamps;
108762306a36Sopenharmony_ci	struct lan966x_phc *phc;
108862306a36Sopenharmony_ci	struct timespec64 ts;
108962306a36Sopenharmony_ci	u64 full_ts_in_ns;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (!lan966x->ptp ||
109262306a36Sopenharmony_ci	    !lan966x->ports[src_port]->ptp_rx_cmd)
109362306a36Sopenharmony_ci		return;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	phc = &lan966x->phc[LAN966X_PHC_PORT];
109662306a36Sopenharmony_ci	lan966x_ptp_gettime64(&phc->info, &ts);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	/* Drop the sub-ns precision */
109962306a36Sopenharmony_ci	timestamp = timestamp >> 2;
110062306a36Sopenharmony_ci	if (ts.tv_nsec < timestamp)
110162306a36Sopenharmony_ci		ts.tv_sec--;
110262306a36Sopenharmony_ci	ts.tv_nsec = timestamp;
110362306a36Sopenharmony_ci	full_ts_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	shhwtstamps = skb_hwtstamps(skb);
110662306a36Sopenharmony_ci	shhwtstamps->hwtstamp = full_ts_in_ns;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ciu32 lan966x_ptp_get_period_ps(void)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	/* This represents the system clock period in picoseconds */
111262306a36Sopenharmony_ci	return 15125;
111362306a36Sopenharmony_ci}
1114