// SPDX-License-Identifier: GPL-2.0+
#include <linux/ptp_classify.h>
#include "lan966x_main.h"
#include "vcap_api.h"
#include "vcap_api_client.h"
#define LAN966X_MAX_PTP_ID 512
/* Represents 1ppm adjustment in 2^59 format with 6.037735849ns as reference
* The value is calculated as following: (1/1000000)/((2^-59)/6.037735849)
*/
#define LAN966X_1PPM_FORMAT 3480517749723LL
/* Represents 1ppb adjustment in 2^29 format with 6.037735849ns as reference
* The value is calculated as following: (1/1000000000)/((2^59)/6.037735849)
*/
#define LAN966X_1PPB_FORMAT 3480517749LL
#define TOD_ACC_PIN 0x7
/* This represents the base rule ID for the PTP rules that are added in the
* VCAP to trap frames to CPU. This number needs to be bigger than the maximum
* number of entries that can exist in the VCAP.
*/
#define LAN966X_VCAP_PTP_RULE_ID 1000000
#define LAN966X_VCAP_L2_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 0)
#define LAN966X_VCAP_IPV4_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 1)
#define LAN966X_VCAP_IPV4_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 2)
#define LAN966X_VCAP_IPV6_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 3)
#define LAN966X_VCAP_IPV6_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 4)
enum {
PTP_PIN_ACTION_IDLE = 0,
PTP_PIN_ACTION_LOAD,
PTP_PIN_ACTION_SAVE,
PTP_PIN_ACTION_CLOCK,
PTP_PIN_ACTION_DELTA,
PTP_PIN_ACTION_TOD
};
static u64 lan966x_ptp_get_nominal_value(void)
{
/* This is the default value that for each system clock, the time of day
* is increased. It has the format 5.59 nanosecond.
*/
return 0x304d4873ecade305;
}
static int lan966x_ptp_add_trap(struct lan966x_port *port,
int (*add_ptp_key)(struct vcap_rule *vrule,
struct lan966x_port*),
u32 rule_id,
u16 proto)
{
struct lan966x *lan966x = port->lan966x;
struct vcap_rule *vrule;
int err;
vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id);
if (!IS_ERR(vrule)) {
u32 value, mask;
/* Just modify the ingress port mask and exit */
vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK,
&value, &mask);
mask &= ~BIT(port->chip_port);
vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK,
value, mask);
err = vcap_mod_rule(vrule);
goto free_rule;
}
vrule = vcap_alloc_rule(lan966x->vcap_ctrl, port->dev,
LAN966X_VCAP_CID_IS2_L0,
VCAP_USER_PTP, 0, rule_id);
if (IS_ERR(vrule))
return PTR_ERR(vrule);
err = add_ptp_key(vrule, port);
if (err)
goto free_rule;
err = vcap_rule_add_action_bit(vrule, VCAP_AF_CPU_COPY_ENA, VCAP_BIT_1);
err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, LAN966X_PMM_REPLACE);
err |= vcap_val_rule(vrule, proto);
if (err)
goto free_rule;
err = vcap_add_rule(vrule);
free_rule:
/* Free the local copy of the rule */
vcap_free_rule(vrule);
return err;
}
static int lan966x_ptp_del_trap(struct lan966x_port *port,
u32 rule_id)
{
struct lan966x *lan966x = port->lan966x;
struct vcap_rule *vrule;
u32 value, mask;
int err;
vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id);
if (IS_ERR(vrule))
return -EEXIST;
vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask);
mask |= BIT(port->chip_port);
/* No other port requires this trap, so it is safe to remove it */
if (mask == GENMASK(lan966x->num_phys_ports, 0)) {
err = vcap_del_rule(lan966x->vcap_ctrl, port->dev, rule_id);
goto free_rule;
}
vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, value, mask);
err = vcap_mod_rule(vrule);
free_rule:
vcap_free_rule(vrule);
return err;
}
static int lan966x_ptp_add_l2_key(struct vcap_rule *vrule,
struct lan966x_port *port)
{
return vcap_rule_add_key_u32(vrule, VCAP_KF_ETYPE, ETH_P_1588, ~0);
}
static int lan966x_ptp_add_ip_event_key(struct vcap_rule *vrule,
struct lan966x_port *port)
{
return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_EV_PORT, ~0) ||
vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0);
}
static int lan966x_ptp_add_ip_general_key(struct vcap_rule *vrule,
struct lan966x_port *port)
{
return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_GEN_PORT, ~0) ||
vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0);
}
static int lan966x_ptp_add_l2_rule(struct lan966x_port *port)
{
return lan966x_ptp_add_trap(port, lan966x_ptp_add_l2_key,
LAN966X_VCAP_L2_PTP_TRAP, ETH_P_ALL);
}
static int lan966x_ptp_add_ipv4_rules(struct lan966x_port *port)
{
int err;
err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key,
LAN966X_VCAP_IPV4_EV_PTP_TRAP, ETH_P_IP);
if (err)
return err;
err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key,
LAN966X_VCAP_IPV4_GEN_PTP_TRAP, ETH_P_IP);
if (err)
lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP);
return err;
}
static int l