return mdiobus_write_nested(priv->parent_bus, addr, regnum, val);
}
+static const struct flow_action_entry *rtldsa_rate_policy_extract(struct flow_cls_offload *cls)
+{
+ struct flow_rule *rule;
+
+ /* only simple rules with a single action are supported */
+ rule = flow_cls_offload_flow_rule(cls);
+
+ if (!flow_action_basic_hw_stats_check(&cls->rule->action,
+ cls->common.extack))
+ return NULL;
+
+ if (!flow_offload_has_one_action(&rule->action))
+ return NULL;
+
+ return &rule->action.entries[0];
+}
+
+static bool rtldsa_port_rate_police_validate(const struct flow_action_entry *act)
+{
+ if (!act)
+ return false;
+
+ /* only allow action which just limit rate with by dropping packets */
+ if (act->id != FLOW_ACTION_POLICE)
+ return false;
+
+ if (act->police.rate_pkt_ps > 0)
+ return false;
+
+ if (act->police.exceed.act_id != FLOW_ACTION_DROP)
+ return false;
+
+ if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT)
+ return false;
+
+ return true;
+}
+
+static int rtldsa_cls_flower_add(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls,
+ bool ingress)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+ struct rtl838x_port *p = &priv->ports[port];
+ const struct flow_action_entry *act;
+ int ret;
+
+ if (!priv->r->port_rate_police_add)
+ return -EOPNOTSUPP;
+
+ /* the single action must be a rate/bandwidth limiter */
+ act = rtldsa_rate_policy_extract(cls);
+
+ if (!rtldsa_port_rate_police_validate(act))
+ return -EOPNOTSUPP;
+
+ mutex_lock(&priv->reg_mutex);
+
+ /* only allow one offloaded police for ingress/egress */
+ if (ingress && p->rate_police_ingress) {
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ if (!ingress && p->rate_police_egress) {
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ ret = priv->r->port_rate_police_add(ds, port, act, ingress);
+ if (ret < 0)
+ goto unlock;
+
+ if (ingress)
+ p->rate_police_ingress = true;
+ else
+ p->rate_police_egress = true;
+
+unlock:
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
+static int rtldsa_cls_flower_del(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls,
+ bool ingress)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+ struct rtl838x_port *p = &priv->ports[port];
+ int ret;
+
+ if (!priv->r->port_rate_police_del)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&priv->reg_mutex);
+
+ ret = priv->r->port_rate_police_del(ds, port, cls, ingress);
+ if (ret < 0)
+ goto unlock;
+
+ if (ingress)
+ p->rate_police_ingress = false;
+ else
+ p->rate_police_egress = false;
+
+unlock:
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
const struct dsa_switch_ops rtl83xx_switch_ops = {
.get_tag_protocol = rtl83xx_get_tag_protocol,
.setup = rtl83xx_setup,
.port_pre_bridge_flags = rtldsa_port_pre_bridge_flags,
.port_bridge_flags = rtl83xx_port_bridge_flags,
+
+ .cls_flower_add = rtldsa_cls_flower_add,
+ .cls_flower_del = rtldsa_cls_flower_del,
};
#define RTL839X_SPCL_TRAP_SWITCH_MAC_CTRL (0x1068)
#define RTL839X_SPCL_TRAP_SWITCH_IPV4_ADDR_CTRL (0x106C)
#define RTL839X_SPCL_TRAP_CRC_CTRL (0x1070)
+
+#define RTL930X_BANDWIDTH_CTRL_EGRESS(port) (0x7660 + (port * 16))
+#define RTL930X_BANDWIDTH_CTRL_INGRESS(port) (0x8068 + (port * 4))
+#define RTL930X_BANDWIDTH_CTRL_MAX_BURST (64 * 1000)
+#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_ON(port) \
+ (0x80DC + (port * 8))
+#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_OFF(port) \
+ (0x80E0 + (port * 8))
+#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_MAX \
+ GENMASK(30, 0)
+
+#define RTL931X_BANDWIDTH_CTRL_EGRESS(port) (0x2164 + (port * 8))
+#define RTL931X_BANDWIDTH_CTRL_INGRESS(port) (0xe008 + (port * 8))
+
+#define RTL93XX_BANDWIDTH_CTRL_RATE_MAX GENMASK(19, 0)
+#define RTL93XX_BANDWIDTH_CTRL_ENABLE BIT(20)
+#define RTL931X_BANDWIDTH_CTRL_MAX_BURST GENMASK(15, 0)
+
+#define RTL930X_INGRESS_FC_CTRL(port) (0x81CC + ((port / 29) * 4))
+#define RTL930X_INGRESS_FC_CTRL_EN(port) BIT(port % 29)
+
/* special port action controls */
/* values:
* 0 = FORWARD (default)
bool is10G:1;
bool is2G5:1;
bool isolated:1;
+ bool rate_police_egress:1;
+ bool rate_police_ingress:1;
u64 pm;
u16 pvid;
bool eee_enabled;
int (*l2_port_new_sa_fwd)(int port);
int (*set_ageing_time)(unsigned long msec);
int (*get_mirror_config)(struct rtldsa_mirror_config *config, int group, int port);
+ int (*port_rate_police_add)(struct dsa_switch *ds, int port,
+ const struct flow_action_entry *act, bool ingress);
+ int (*port_rate_police_del)(struct dsa_switch *ds, int port, struct flow_cls_offload *cls,
+ bool ingress);
u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e);
void (*write_l2_entry_using_hash)(u32 hash, u32 pos, struct rtl838x_l2_entry *e);
u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e);
return 0;
}
+static int rtldsa_930x_port_rate_police_add(struct dsa_switch *ds, int port,
+ const struct flow_action_entry *act,
+ bool ingress)
+{
+ u32 burst;
+ u64 rate;
+ u32 addr;
+
+ /* rate has unit 16000 bit */
+ rate = div_u64(act->police.rate_bytes_ps, 2000);
+ rate = min_t(u64, rate, RTL93XX_BANDWIDTH_CTRL_RATE_MAX);
+ rate |= RTL93XX_BANDWIDTH_CTRL_ENABLE;
+
+ if (ingress)
+ addr = RTL930X_BANDWIDTH_CTRL_INGRESS(port);
+ else
+ addr = RTL930X_BANDWIDTH_CTRL_EGRESS(port);
+
+ if (ingress) {
+ burst = min_t(u32, act->police.burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_MAX);
+
+ /* set burst high on/off the same to avoid TCP oscillation */
+ sw_w32(burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_ON(port));
+ sw_w32(burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_OFF(port));
+
+ /* Enable ingress bandwidth flow control to improve TCP throughput and avoid
+ * the drops behavior of the RTL930x ingress rate limiter which seem to not
+ * play well with any congestion control algorithm
+ */
+ sw_w32_mask(0, RTL930X_INGRESS_FC_CTRL_EN(port),
+ RTL930X_INGRESS_FC_CTRL(port));
+ } else {
+ burst = min_t(u32, act->police.burst, RTL930X_BANDWIDTH_CTRL_MAX_BURST);
+
+ sw_w32(burst, addr + 4);
+ }
+
+ sw_w32(rate, addr);
+
+ return 0;
+}
+
+static int rtldsa_930x_port_rate_police_del(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls,
+ bool ingress)
+{
+ u32 addr;
+
+ if (ingress)
+ addr = RTL930X_BANDWIDTH_CTRL_INGRESS(port);
+ else
+ addr = RTL930X_BANDWIDTH_CTRL_EGRESS(port);
+
+ sw_w32_mask(RTL93XX_BANDWIDTH_CTRL_ENABLE, 0, addr);
+
+ if (ingress)
+ sw_w32_mask(RTL930X_INGRESS_FC_CTRL_EN(port), 0,
+ RTL930X_INGRESS_FC_CTRL(port));
+
+ return 0;
+}
+
inline static int rtl930x_trk_mbr_ctr(int group)
{
return RTL930X_TRK_MBR_CTRL + (group << 2);
.l2_port_new_salrn = rtl930x_l2_port_new_salrn,
.l2_port_new_sa_fwd = rtl930x_l2_port_new_sa_fwd,
.get_mirror_config = rtldsa_930x_get_mirror_config,
+ .port_rate_police_add = rtldsa_930x_port_rate_police_add,
+ .port_rate_police_del = rtldsa_930x_port_rate_police_del,
.read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash,
.write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash,
.read_cam = rtl930x_read_cam,
return 0;
}
+static int rtldsa_931x_port_rate_police_add(struct dsa_switch *ds, int port,
+ const struct flow_action_entry *act,
+ bool ingress)
+{
+ u32 burst;
+ u64 rate;
+ u32 addr;
+
+ /* rate has unit 16000 bit */
+ rate = div_u64(act->police.rate_bytes_ps, 2000);
+ rate = min_t(u64, rate, RTL93XX_BANDWIDTH_CTRL_RATE_MAX);
+ rate |= RTL93XX_BANDWIDTH_CTRL_ENABLE;
+
+ burst = min_t(u32, act->police.burst, RTL931X_BANDWIDTH_CTRL_MAX_BURST);
+
+ if (ingress)
+ addr = RTL931X_BANDWIDTH_CTRL_INGRESS(port);
+ else
+ addr = RTL931X_BANDWIDTH_CTRL_EGRESS(port);
+
+ sw_w32(burst, addr + 4);
+ sw_w32(rate, addr);
+
+ return 0;
+}
+
+static int rtldsa_931x_port_rate_police_del(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls,
+ bool ingress)
+{
+ u32 addr;
+
+ if (ingress)
+ addr = RTL931X_BANDWIDTH_CTRL_INGRESS(port);
+ else
+ addr = RTL931X_BANDWIDTH_CTRL_EGRESS(port);
+
+ sw_w32_mask(RTL93XX_BANDWIDTH_CTRL_ENABLE, 0, addr);
+
+ return 0;
+}
+
irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
{
struct dsa_switch *ds = dev_id;
.l2_port_new_salrn = rtl931x_l2_port_new_salrn,
.l2_port_new_sa_fwd = rtl931x_l2_port_new_sa_fwd,
.get_mirror_config = rtldsa_931x_get_mirror_config,
+ .port_rate_police_add = rtldsa_931x_port_rate_police_add,
+ .port_rate_police_del = rtldsa_931x_port_rate_police_del,
.read_l2_entry_using_hash = rtl931x_read_l2_entry_using_hash,
.write_l2_entry_using_hash = rtl931x_write_l2_entry_using_hash,
.read_cam = rtl931x_read_cam,