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