Commit b41dc4ae authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-dsa-felix-tc-taprio-and-CBS-offload-support'



Xiaoliang Yang says:

====================
net: dsa: felix: tc taprio and CBS offload support

This patch series support tc taprio and CBS hardware offload according
to IEEE 802.1Qbv and IEEE-802.1Qav on VSC9959.

v1->v2 changes:
 - Move port_qos_map_init() function to be common felix codes.
 - Keep const for dsa_switch_ops structs, add felix_port_setup_tc
   function to call port_setup_tc of felix.info.
 - fix code style for cbs_set, rename variables.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents fb9f2e92 0fbabf87
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -237,6 +237,10 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port,

	if (felix->info->pcs_init)
		felix->info->pcs_init(ocelot, port, link_an_mode, state);

	if (felix->info->port_sched_speed_set)
		felix->info->port_sched_speed_set(ocelot, port,
						  state->speed);
}

static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port)
@@ -289,6 +293,27 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
			 QSYS_SWITCH_PORT_MODE, port);
}

static void felix_port_qos_map_init(struct ocelot *ocelot, int port)
{
	int i;

	ocelot_rmw_gix(ocelot,
		       ANA_PORT_QOS_CFG_QOS_PCP_ENA,
		       ANA_PORT_QOS_CFG_QOS_PCP_ENA,
		       ANA_PORT_QOS_CFG,
		       port);

	for (i = 0; i < FELIX_NUM_TC * 2; i++) {
		ocelot_rmw_ix(ocelot,
			      (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) |
			      ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i),
			      ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL |
			      ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL_M,
			      ANA_PORT_PCP_DEI_MAP,
			      port, i);
	}
}

static void felix_get_strings(struct dsa_switch *ds, int port,
			      u32 stringset, u8 *data)
{
@@ -547,6 +572,11 @@ static int felix_setup(struct dsa_switch *ds)
			ocelot_configure_cpu(ocelot, port,
					     OCELOT_TAG_PREFIX_NONE,
					     OCELOT_TAG_PREFIX_LONG);

		/* Set the default QoS Classification based on PCP and DEI
		 * bits of vlan tag.
		 */
		felix_port_qos_map_init(ocelot, port);
	}

	/* Include the CPU port module in the forwarding mask for unknown
@@ -704,6 +734,19 @@ static void felix_port_policer_del(struct dsa_switch *ds, int port)
	ocelot_port_policer_del(ocelot, port);
}

static int felix_port_setup_tc(struct dsa_switch *ds, int port,
			       enum tc_setup_type type,
			       void *type_data)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);

	if (felix->info->port_setup_tc)
		return felix->info->port_setup_tc(ds, port, type, type_data);
	else
		return -EOPNOTSUPP;
}

static const struct dsa_switch_ops felix_switch_ops = {
	.get_tag_protocol	= felix_get_tag_protocol,
	.setup			= felix_setup,
@@ -742,6 +785,7 @@ static const struct dsa_switch_ops felix_switch_ops = {
	.cls_flower_add		= felix_cls_flower_add,
	.cls_flower_del		= felix_cls_flower_del,
	.cls_flower_stats	= felix_cls_flower_stats,
	.port_setup_tc          = felix_port_setup_tc,
};

static struct felix_info *felix_instance_tbl[] = {
@@ -830,6 +874,7 @@ static int felix_pci_probe(struct pci_dev *pdev,

	ds->dev = &pdev->dev;
	ds->num_ports = felix->info->num_ports;
	ds->num_tx_queues = felix->info->num_tx_queues;
	ds->ops = &felix_switch_ops;
	ds->priv = ocelot;
	felix->ds = ds;
+5 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ struct felix_info {
	const struct ocelot_stat_layout	*stats_layout;
	unsigned int			num_stats;
	int				num_ports;
	int                             num_tx_queues;
	struct vcap_field		*vcap_is2_keys;
	struct vcap_field		*vcap_is2_actions;
	const struct vcap_props		*vcap;
@@ -35,6 +36,10 @@ struct felix_info {
				  struct phylink_link_state *state);
	int	(*prevalidate_phy_mode)(struct ocelot *ocelot, int port,
					phy_interface_t phy_mode);
	int	(*port_setup_tc)(struct dsa_switch *ds, int port,
				 enum tc_setup_type type, void *type_data);
	void	(*port_sched_speed_set)(struct ocelot *ocelot, int port,
					u32 speed);
};

extern struct felix_info		felix_info_vsc9959;
+189 −1
Original line number Diff line number Diff line
@@ -3,9 +3,12 @@
 * Copyright 2018-2019 NXP Semiconductors
 */
#include <linux/fsl/enetc_mdio.h>
#include <soc/mscc/ocelot_qsys.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot.h>
#include <net/pkt_sched.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include "felix.h"
@@ -27,6 +30,8 @@
#define USXGMII_LPA_DUPLEX(lpa)		(((lpa) & GENMASK(12, 12)) >> 12)
#define USXGMII_LPA_SPEED(lpa)		(((lpa) & GENMASK(11, 9)) >> 9)

#define VSC9959_TAS_GCL_ENTRY_MAX	63

enum usxgmii_speed {
	USXGMII_SPEED_10	= 0,
	USXGMII_SPEED_100	= 1,
@@ -202,7 +207,7 @@ static const u32 vsc9959_qsys_regmap[] = {
	REG(QSYS_QMAXSDU_CFG_6,			0x00f62c),
	REG(QSYS_QMAXSDU_CFG_7,			0x00f648),
	REG(QSYS_PREEMPTION_CFG,		0x00f664),
	REG_RESERVED(QSYS_CIR_CFG),
	REG(QSYS_CIR_CFG,			0x000000),
	REG(QSYS_EIR_CFG,			0x000004),
	REG(QSYS_SE_CFG,			0x000008),
	REG(QSYS_SE_DWRR_CFG,			0x00000c),
@@ -1209,6 +1214,186 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
	mdiobus_unregister(felix->imdio);
}

static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
				    u32 speed)
{
	ocelot_rmw_rix(ocelot,
		       QSYS_TAG_CONFIG_LINK_SPEED(speed),
		       QSYS_TAG_CONFIG_LINK_SPEED_M,
		       QSYS_TAG_CONFIG, port);
}

static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
				  u64 cycle_time,
				  struct timespec64 *new_base_ts)
{
	struct timespec64 ts;
	ktime_t new_base_time;
	ktime_t current_time;

	ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
	current_time = timespec64_to_ktime(ts);
	new_base_time = base_time;

	if (base_time < current_time) {
		u64 nr_of_cycles = current_time - base_time;

		do_div(nr_of_cycles, cycle_time);
		new_base_time += cycle_time * (nr_of_cycles + 1);
	}

	*new_base_ts = ktime_to_timespec64(new_base_time);
}

static u32 vsc9959_tas_read_cfg_status(struct ocelot *ocelot)
{
	return ocelot_read(ocelot, QSYS_TAS_PARAM_CFG_CTRL);
}

static void vsc9959_tas_gcl_set(struct ocelot *ocelot, const u32 gcl_ix,
				struct tc_taprio_sched_entry *entry)
{
	ocelot_write(ocelot,
		     QSYS_GCL_CFG_REG_1_GCL_ENTRY_NUM(gcl_ix) |
		     QSYS_GCL_CFG_REG_1_GATE_STATE(entry->gate_mask),
		     QSYS_GCL_CFG_REG_1);
	ocelot_write(ocelot, entry->interval, QSYS_GCL_CFG_REG_2);
}

static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
				    struct tc_taprio_qopt_offload *taprio)
{
	struct timespec64 base_ts;
	int ret, i;
	u32 val;

	if (!taprio->enable) {
		ocelot_rmw_rix(ocelot,
			       QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF),
			       QSYS_TAG_CONFIG_ENABLE |
			       QSYS_TAG_CONFIG_INIT_GATE_STATE_M,
			       QSYS_TAG_CONFIG, port);

		return 0;
	}

	if (taprio->cycle_time > NSEC_PER_SEC ||
	    taprio->cycle_time_extension >= NSEC_PER_SEC)
		return -EINVAL;

	if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX)
		return -ERANGE;

	ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(port) |
		   QSYS_TAS_PARAM_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q,
		   QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M |
		   QSYS_TAS_PARAM_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q,
		   QSYS_TAS_PARAM_CFG_CTRL);

	/* Hardware errata -  Admin config could not be overwritten if
	 * config is pending, need reset the TAS module
	 */
	val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8);
	if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING)
		return  -EBUSY;

	ocelot_rmw_rix(ocelot,
		       QSYS_TAG_CONFIG_ENABLE |
		       QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF) |
		       QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES(0xFF),
		       QSYS_TAG_CONFIG_ENABLE |
		       QSYS_TAG_CONFIG_INIT_GATE_STATE_M |
		       QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_M,
		       QSYS_TAG_CONFIG, port);

	vsc9959_new_base_time(ocelot, taprio->base_time,
			      taprio->cycle_time, &base_ts);
	ocelot_write(ocelot, base_ts.tv_nsec, QSYS_PARAM_CFG_REG_1);
	ocelot_write(ocelot, lower_32_bits(base_ts.tv_sec), QSYS_PARAM_CFG_REG_2);
	val = upper_32_bits(base_ts.tv_sec);
	ocelot_write(ocelot,
		     QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB(val) |
		     QSYS_PARAM_CFG_REG_3_LIST_LENGTH(taprio->num_entries),
		     QSYS_PARAM_CFG_REG_3);
	ocelot_write(ocelot, taprio->cycle_time, QSYS_PARAM_CFG_REG_4);
	ocelot_write(ocelot, taprio->cycle_time_extension, QSYS_PARAM_CFG_REG_5);

	for (i = 0; i < taprio->num_entries; i++)
		vsc9959_tas_gcl_set(ocelot, i, &taprio->entries[i]);

	ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
		   QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
		   QSYS_TAS_PARAM_CFG_CTRL);

	ret = readx_poll_timeout(vsc9959_tas_read_cfg_status, ocelot, val,
				 !(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE),
				 10, 100000);

	return ret;
}

static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port,
				    struct tc_cbs_qopt_offload *cbs_qopt)
{
	struct ocelot *ocelot = ds->priv;
	int port_ix = port * 8 + cbs_qopt->queue;
	u32 rate, burst;

	if (cbs_qopt->queue >= ds->num_tx_queues)
		return -EINVAL;

	if (!cbs_qopt->enable) {
		ocelot_write_gix(ocelot, QSYS_CIR_CFG_CIR_RATE(0) |
				 QSYS_CIR_CFG_CIR_BURST(0),
				 QSYS_CIR_CFG, port_ix);

		ocelot_rmw_gix(ocelot, 0, QSYS_SE_CFG_SE_AVB_ENA,
			       QSYS_SE_CFG, port_ix);

		return 0;
	}

	/* Rate unit is 100 kbps */
	rate = DIV_ROUND_UP(cbs_qopt->idleslope, 100);
	/* Avoid using zero rate */
	rate = clamp_t(u32, rate, 1, GENMASK(14, 0));
	/* Burst unit is 4kB */
	burst = DIV_ROUND_UP(cbs_qopt->hicredit, 4096);
	/* Avoid using zero burst size */
	burst = clamp_t(u32, rate, 1, GENMASK(5, 0));
	ocelot_write_gix(ocelot,
			 QSYS_CIR_CFG_CIR_RATE(rate) |
			 QSYS_CIR_CFG_CIR_BURST(burst),
			 QSYS_CIR_CFG,
			 port_ix);

	ocelot_rmw_gix(ocelot,
		       QSYS_SE_CFG_SE_FRM_MODE(0) |
		       QSYS_SE_CFG_SE_AVB_ENA,
		       QSYS_SE_CFG_SE_AVB_ENA |
		       QSYS_SE_CFG_SE_FRM_MODE_M,
		       QSYS_SE_CFG,
		       port_ix);

	return 0;
}

static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,
				 enum tc_setup_type type,
				 void *type_data)
{
	struct ocelot *ocelot = ds->priv;

	switch (type) {
	case TC_SETUP_QDISC_TAPRIO:
		return vsc9959_qos_port_tas_set(ocelot, port, type_data);
	case TC_SETUP_QDISC_CBS:
		return vsc9959_qos_port_cbs_set(ds, port, type_data);
	default:
		return -EOPNOTSUPP;
	}
}

struct felix_info felix_info_vsc9959 = {
	.target_io_res		= vsc9959_target_io_res,
	.port_io_res		= vsc9959_port_io_res,
@@ -1224,6 +1409,7 @@ struct felix_info felix_info_vsc9959 = {
	.shared_queue_sz	= 128 * 1024,
	.num_mact_rows		= 2048,
	.num_ports		= 6,
	.num_tx_queues		= FELIX_NUM_TC,
	.switch_pci_bar		= 4,
	.imdio_pci_bar		= 0,
	.mdio_bus_alloc		= vsc9959_mdio_bus_alloc,
@@ -1232,4 +1418,6 @@ struct felix_info felix_info_vsc9959 = {
	.pcs_an_restart		= vsc9959_pcs_an_restart,
	.pcs_link_state		= vsc9959_pcs_link_state,
	.prevalidate_phy_mode	= vsc9959_prevalidate_phy_mode,
	.port_setup_tc          = vsc9959_port_setup_tc,
	.port_sched_speed_set   = vsc9959_sched_speed_set,
};