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

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
......@@ -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;
......
......@@ -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;
......
......@@ -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,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment