diff options
Diffstat (limited to 'drivers/net/dsa/ocelot/felix_vsc9959.c')
| -rw-r--r-- | drivers/net/dsa/ocelot/felix_vsc9959.c | 336 |
1 files changed, 194 insertions, 142 deletions
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 01ac70fd7ddf..8cf4c8986587 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -6,6 +6,7 @@ #include <soc/mscc/ocelot_qsys.h> #include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_ana.h> +#include <soc/mscc/ocelot_dev.h> #include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_sys.h> #include <net/tc_act/tc_gate.h> @@ -15,6 +16,7 @@ #include <net/pkt_sched.h> #include <linux/iopoll.h> #include <linux/mdio.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/time.h> #include "felix.h" @@ -22,7 +24,7 @@ #define VSC9959_NUM_PORTS 6 #define VSC9959_TAS_GCL_ENTRY_MAX 63 -#define VSC9959_TAS_MIN_GATE_LEN_NS 33 +#define VSC9959_TAS_MIN_GATE_LEN_NS 35 #define VSC9959_VCAP_POLICER_BASE 63 #define VSC9959_VCAP_POLICER_MAX 383 #define VSC9959_SWITCH_PCI_BAR 4 @@ -32,7 +34,8 @@ OCELOT_PORT_MODE_QSGMII | \ OCELOT_PORT_MODE_1000BASEX | \ OCELOT_PORT_MODE_2500BASEX | \ - OCELOT_PORT_MODE_USXGMII) + OCELOT_PORT_MODE_USXGMII | \ + OCELOT_PORT_MODE_10G_QXGMII) static const u32 vsc9959_port_modes[VSC9959_NUM_PORTS] = { VSC9959_PORT_MODE_SERDES, @@ -318,6 +321,29 @@ static const u32 vsc9959_sys_regmap[] = { REG(SYS_COUNT_RX_GREEN_PRIO_5, 0x0000a4), REG(SYS_COUNT_RX_GREEN_PRIO_6, 0x0000a8), REG(SYS_COUNT_RX_GREEN_PRIO_7, 0x0000ac), + REG(SYS_COUNT_RX_ASSEMBLY_ERRS, 0x0000b0), + REG(SYS_COUNT_RX_SMD_ERRS, 0x0000b4), + REG(SYS_COUNT_RX_ASSEMBLY_OK, 0x0000b8), + REG(SYS_COUNT_RX_MERGE_FRAGMENTS, 0x0000bc), + REG(SYS_COUNT_RX_PMAC_OCTETS, 0x0000c0), + REG(SYS_COUNT_RX_PMAC_UNICAST, 0x0000c4), + REG(SYS_COUNT_RX_PMAC_MULTICAST, 0x0000c8), + REG(SYS_COUNT_RX_PMAC_BROADCAST, 0x0000cc), + REG(SYS_COUNT_RX_PMAC_SHORTS, 0x0000d0), + REG(SYS_COUNT_RX_PMAC_FRAGMENTS, 0x0000d4), + REG(SYS_COUNT_RX_PMAC_JABBERS, 0x0000d8), + REG(SYS_COUNT_RX_PMAC_CRC_ALIGN_ERRS, 0x0000dc), + REG(SYS_COUNT_RX_PMAC_SYM_ERRS, 0x0000e0), + REG(SYS_COUNT_RX_PMAC_64, 0x0000e4), + REG(SYS_COUNT_RX_PMAC_65_127, 0x0000e8), + REG(SYS_COUNT_RX_PMAC_128_255, 0x0000ec), + REG(SYS_COUNT_RX_PMAC_256_511, 0x0000f0), + REG(SYS_COUNT_RX_PMAC_512_1023, 0x0000f4), + REG(SYS_COUNT_RX_PMAC_1024_1526, 0x0000f8), + REG(SYS_COUNT_RX_PMAC_1527_MAX, 0x0000fc), + REG(SYS_COUNT_RX_PMAC_PAUSE, 0x000100), + REG(SYS_COUNT_RX_PMAC_CONTROL, 0x000104), + REG(SYS_COUNT_RX_PMAC_LONGS, 0x000108), REG(SYS_COUNT_TX_OCTETS, 0x000200), REG(SYS_COUNT_TX_UNICAST, 0x000204), REG(SYS_COUNT_TX_MULTICAST, 0x000208), @@ -349,6 +375,20 @@ static const u32 vsc9959_sys_regmap[] = { REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000270), REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000274), REG(SYS_COUNT_TX_AGED, 0x000278), + REG(SYS_COUNT_TX_MM_HOLD, 0x00027c), + REG(SYS_COUNT_TX_MERGE_FRAGMENTS, 0x000280), + REG(SYS_COUNT_TX_PMAC_OCTETS, 0x000284), + REG(SYS_COUNT_TX_PMAC_UNICAST, 0x000288), + REG(SYS_COUNT_TX_PMAC_MULTICAST, 0x00028c), + REG(SYS_COUNT_TX_PMAC_BROADCAST, 0x000290), + REG(SYS_COUNT_TX_PMAC_PAUSE, 0x000294), + REG(SYS_COUNT_TX_PMAC_64, 0x000298), + REG(SYS_COUNT_TX_PMAC_65_127, 0x00029c), + REG(SYS_COUNT_TX_PMAC_128_255, 0x0002a0), + REG(SYS_COUNT_TX_PMAC_256_511, 0x0002a4), + REG(SYS_COUNT_TX_PMAC_512_1023, 0x0002a8), + REG(SYS_COUNT_TX_PMAC_1024_1526, 0x0002ac), + REG(SYS_COUNT_TX_PMAC_1527_MAX, 0x0002b0), REG(SYS_COUNT_DROP_LOCAL, 0x000400), REG(SYS_COUNT_DROP_TAIL, 0x000404), REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000408), @@ -439,6 +479,9 @@ static const u32 vsc9959_dev_gmii_regmap[] = { REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c), REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40), REG(DEV_MAC_STICKY, 0x44), + REG(DEV_MM_ENABLE_CONFIG, 0x48), + REG(DEV_MM_VERIF_CONFIG, 0x4C), + REG(DEV_MM_STATUS, 0x50), REG_RESERVED(PCS1G_CFG), REG_RESERVED(PCS1G_MODE_CFG), REG_RESERVED(PCS1G_SD_CFG), @@ -513,7 +556,7 @@ static const char * const vsc9959_resource_names[TARGET_MAX] = { * SGMII/QSGMII MAC PCS can be found. */ static const struct resource vsc9959_imdio_res = - DEFINE_RES_MEM_NAMED(0x8030, 0x8040, "imdio"); + DEFINE_RES_MEM_NAMED(0x8030, 0x10, "imdio"); static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = { [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6), @@ -954,8 +997,10 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) return -ENOMEM; bus->name = "VSC9959 internal MDIO bus"; - bus->read = enetc_mdio_read; - bus->write = enetc_mdio_write; + bus->read = enetc_mdio_read_c22; + bus->write = enetc_mdio_write_c22; + bus->read_c45 = enetc_mdio_read_c45; + bus->write_c45 = enetc_mdio_write_c45; bus->parent = dev; mdio_priv = bus->priv; mdio_priv->hw = hw; @@ -978,7 +1023,6 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) for (port = 0; port < felix->info->num_ports; port++) { struct ocelot_port *ocelot_port = ocelot->ports[port]; struct phylink_pcs *phylink_pcs; - struct mdio_device *mdio_device; if (dsa_is_unused_port(felix->ds, port)) continue; @@ -986,16 +1030,10 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL) continue; - mdio_device = mdio_device_create(felix->imdio, port); - if (IS_ERR(mdio_device)) + phylink_pcs = lynx_pcs_create_mdiodev(felix->imdio, port); + if (IS_ERR(phylink_pcs)) continue; - phylink_pcs = lynx_pcs_create(mdio_device); - if (!phylink_pcs) { - mdio_device_free(mdio_device); - continue; - } - felix->pcs[port] = phylink_pcs; dev_info(dev, "Found PCS at internal MDIO address %d\n", port); @@ -1011,24 +1049,23 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot) for (port = 0; port < ocelot->num_phys_ports; port++) { struct phylink_pcs *phylink_pcs = felix->pcs[port]; - struct mdio_device *mdio_device; - if (!phylink_pcs) - continue; - - mdio_device = lynx_get_mdio_device(phylink_pcs); - mdio_device_free(mdio_device); - lynx_pcs_destroy(phylink_pcs); + if (phylink_pcs) + lynx_pcs_destroy(phylink_pcs); } mdiobus_unregister(felix->imdio); mdiobus_free(felix->imdio); } -/* The switch considers any frame (regardless of size) as eligible for - * transmission if the traffic class gate is open for at least 33 ns. +/* The switch considers any frame (regardless of size) as eligible + * for transmission if the traffic class gate is open for at least + * VSC9959_TAS_MIN_GATE_LEN_NS. + * * Overruns are prevented by cropping an interval at the end of the gate time - * slot for which egress scheduling is blocked, but we need to still keep 33 ns - * available for one packet to be transmitted, otherwise the port tc will hang. + * slot for which egress scheduling is blocked, but we need to still keep + * VSC9959_TAS_MIN_GATE_LEN_NS available for one packet to be transmitted, + * otherwise the port tc will hang. + * * This function returns the size of a gate interval that remains available for * setting the guard band, after reserving the space for one egress frame. */ @@ -1038,6 +1075,9 @@ static u64 vsc9959_tas_remaining_gate_len_ps(u64 gate_len_ns) if (gate_len_ns == U64_MAX) return U64_MAX; + if (gate_len_ns < VSC9959_TAS_MIN_GATE_LEN_NS) + return 0; + return (gate_len_ns - VSC9959_TAS_MIN_GATE_LEN_NS) * PSEC_PER_NSEC; } @@ -1178,15 +1218,17 @@ static u32 vsc9959_tas_tc_max_sdu(struct tc_taprio_qopt_offload *taprio, int tc) static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_mm_state *mm = &ocelot->mm[port]; struct tc_taprio_qopt_offload *taprio; u64 min_gate_len[OCELOT_NUM_TC]; + u32 val, maxlen, add_frag_size; + u64 needed_min_frag_time_ps; int speed, picos_per_byte; u64 needed_bit_time_ps; - u32 val, maxlen; u8 tas_speed; int tc; - lockdep_assert_held(&ocelot->tas_lock); + lockdep_assert_held(&ocelot->fwd_domain_lock); taprio = ocelot_port->taprio; @@ -1220,16 +1262,23 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) /* Consider the standard Ethernet overhead of 8 octets preamble+SFD, * 4 octets FCS, 12 octets IFG. */ - needed_bit_time_ps = (maxlen + 24) * picos_per_byte; + needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte; + + /* Preemptible TCs don't need to pass a full MTU, the port will + * automatically emit a HOLD request when a preemptible TC gate closes + */ + val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); + add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); + needed_min_frag_time_ps = picos_per_byte * + (u64)(24 + 2 * ethtool_mm_frag_size_add_to_min(add_frag_size)); dev_dbg(ocelot->dev, - "port %d: max frame size %d needs %llu ps at speed %d\n", - port, maxlen, needed_bit_time_ps, speed); + "port %d: max frame size %d needs %llu ps, %llu ps for mPackets at speed %d\n", + port, maxlen, needed_bit_time_ps, needed_min_frag_time_ps, + speed); vsc9959_tas_min_gate_lengths(taprio, min_gate_len); - mutex_lock(&ocelot->fwd_domain_lock); - for (tc = 0; tc < OCELOT_NUM_TC; tc++) { u32 requested_max_sdu = vsc9959_tas_tc_max_sdu(taprio, tc); u64 remaining_gate_len_ps; @@ -1238,7 +1287,9 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) remaining_gate_len_ps = vsc9959_tas_remaining_gate_len_ps(min_gate_len[tc]); - if (remaining_gate_len_ps > needed_bit_time_ps) { + if ((mm->active_preemptible_tcs & BIT(tc)) ? + remaining_gate_len_ps > needed_min_frag_time_ps : + remaining_gate_len_ps > needed_bit_time_ps) { /* Setting QMAXSDU_CFG to 0 disables oversized frame * dropping. */ @@ -1257,7 +1308,8 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) * per-tc static guard band lengths, so it reduces the * useful gate interval length. Therefore, be careful * to calculate a guard band (and therefore max_sdu) - * that still leaves 33 ns available in the time slot. + * that still leaves VSC9959_TAS_MIN_GATE_LEN_NS + * available in the time slot. */ max_sdu = div_u64(remaining_gate_len_ps, picos_per_byte); /* A TC gate may be completely closed, which is a @@ -1292,8 +1344,6 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) ocelot_write_rix(ocelot, maxlen, QSYS_PORT_MAX_SDU, port); ocelot->ops->cut_through_fwd(ocelot); - - mutex_unlock(&ocelot->fwd_domain_lock); } static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, @@ -1320,7 +1370,7 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, break; } - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); ocelot_rmw_rix(ocelot, QSYS_TAG_CONFIG_LINK_SPEED(tas_speed), @@ -1330,7 +1380,7 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, if (ocelot_port->taprio) vsc9959_tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); } static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time, @@ -1378,9 +1428,10 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, int ret, i; u32 val; - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); - if (!taprio->enable) { + if (taprio->cmd == TAPRIO_CMD_DESTROY) { + ocelot_port_mqprio(ocelot, port, &taprio->mqprio); ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, QSYS_TAG_CONFIG, port); @@ -1389,19 +1440,26 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, vsc9959_tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); return 0; + } else if (taprio->cmd != TAPRIO_CMD_REPLACE) { + ret = -EOPNOTSUPP; + goto err_unlock; } + ret = ocelot_port_mqprio(ocelot, port, &taprio->mqprio); + if (ret) + goto err_unlock; + if (taprio->cycle_time > NSEC_PER_SEC || taprio->cycle_time_extension >= NSEC_PER_SEC) { ret = -EINVAL; - goto err; + goto err_reset_tc; } if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX) { ret = -ERANGE; - goto err; + goto err_reset_tc; } /* Enable guard band. The switch will schedule frames without taking @@ -1422,10 +1480,13 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, /* 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) { - ret = -EBUSY; - goto err; + val = ocelot_read_rix(ocelot, QSYS_TAG_CONFIG, port); + if (val & QSYS_TAG_CONFIG_ENABLE) { + val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8); + if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING) { + ret = -EBUSY; + goto err_reset_tc; + } } ocelot_rmw_rix(ocelot, @@ -1460,13 +1521,20 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, !(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE), 10, 100000); if (ret) - goto err; + goto err_reset_tc; ocelot_port->taprio = taprio_offload_get(taprio); vsc9959_tas_guard_bands_update(ocelot, port); -err: - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); + + return 0; + +err_reset_tc: + taprio->mqprio.qopt.num_tc = 0; + ocelot_port_mqprio(ocelot, port, &taprio->mqprio); +err_unlock: + mutex_unlock(&ocelot->fwd_domain_lock); return ret; } @@ -1476,10 +1544,10 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) struct tc_taprio_qopt_offload *taprio; struct ocelot_port *ocelot_port; struct timespec64 base_ts; - int port; + int i, port; u32 val; - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); for (port = 0; port < ocelot->num_phys_ports; port++) { ocelot_port = ocelot->ports[port]; @@ -1508,6 +1576,9 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB_M, QSYS_PARAM_CFG_REG_3); + 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); @@ -1517,7 +1588,7 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) QSYS_TAG_CONFIG_ENABLE, QSYS_TAG_CONFIG, port); } - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); } static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port, @@ -1569,6 +1640,13 @@ static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port, static int vsc9959_qos_query_caps(struct tc_query_caps_base *base) { switch (base->type) { + case TC_SETUP_QDISC_MQPRIO: { + struct tc_mqprio_caps *caps = base->caps; + + caps->validate_queue_counts = true; + + return 0; + } case TC_SETUP_QDISC_TAPRIO: { struct tc_taprio_caps *caps = base->caps; @@ -1581,6 +1659,18 @@ static int vsc9959_qos_query_caps(struct tc_query_caps_base *base) } } +static int vsc9959_qos_port_mqprio(struct ocelot *ocelot, int port, + struct tc_mqprio_qopt_offload *mqprio) +{ + int ret; + + mutex_lock(&ocelot->fwd_domain_lock); + ret = ocelot_port_mqprio(ocelot, port, mqprio); + mutex_unlock(&ocelot->fwd_domain_lock); + + return ret; +} + static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) @@ -1592,6 +1682,8 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, return vsc9959_qos_query_caps(type_data); case TC_SETUP_QDISC_TAPRIO: return vsc9959_qos_port_tas_set(ocelot, port, type_data); + case TC_SETUP_QDISC_MQPRIO: + return vsc9959_qos_port_mqprio(ocelot, port, type_data); case TC_SETUP_QDISC_CBS: return vsc9959_qos_port_cbs_set(ds, port, type_data); default: @@ -1653,7 +1745,7 @@ struct felix_stream_gate { u64 cycletime; u64 cycletime_ext; u32 num_entries; - struct action_gate_entry entries[]; + struct action_gate_entry entries[] __counted_by(num_entries); }; struct felix_stream_gate_entry { @@ -1669,10 +1761,13 @@ static int vsc9959_stream_identify(struct flow_cls_offload *f, struct flow_dissector *dissector = rule->match.dissector; if (dissector->used_keys & - ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | - BIT(FLOW_DISSECTOR_KEY_BASIC) | - BIT(FLOW_DISSECTOR_KEY_VLAN) | - BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) + ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) + return -EOPNOTSUPP; + + if (flow_rule_match_has_control_flags(rule, f->common.extack)) return -EOPNOTSUPP; if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { @@ -2455,6 +2550,7 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot) for (port = 0; port < ocelot->num_phys_ports; port++) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_mm_state *mm = &ocelot->mm[port]; int min_speed = ocelot_port->speed; unsigned long mask = 0; u32 tmp, val = 0; @@ -2495,10 +2591,12 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot) /* Enable cut-through forwarding for all traffic classes that * don't have oversized dropping enabled, since this check is - * bypassed in cut-through mode. + * bypassed in cut-through mode. Also exclude preemptible + * traffic classes, since these would hang the port for some + * reason, if sent as cut-through. */ if (ocelot_port->speed == min_speed) { - val = GENMASK(7, 0); + val = GENMASK(7, 0) & ~mm->active_preemptible_tcs; for (tc = 0; tc < OCELOT_NUM_TC; tc++) if (vsc9959_port_qmaxsdu_get(ocelot, port, tc)) @@ -2519,6 +2617,28 @@ set: } } +/* The INTB interrupt is shared between for PTP TX timestamp availability + * notification and MAC Merge status change on each port. + */ +static irqreturn_t vsc9959_irq_handler(int irq, void *data) +{ + struct ocelot *ocelot = data; + + ocelot_get_txtstamp(ocelot); + ocelot_mm_irq(ocelot); + + return IRQ_HANDLED; +} + +static int vsc9959_request_irq(struct ocelot *ocelot) +{ + struct pci_dev *pdev = to_pci_dev(ocelot->dev); + + return devm_request_threaded_irq(ocelot->dev, pdev->irq, NULL, + &vsc9959_irq_handler, IRQF_ONESHOT, + "felix-intb", ocelot); +} + static const struct ocelot_ops vsc9959_ops = { .reset = vsc9959_reset, .wm_enc = vsc9959_wm_enc, @@ -2533,6 +2653,7 @@ static const struct ocelot_ops vsc9959_ops = { .cut_through_fwd = vsc9959_cut_through_fwd, .tas_clock_adjust = vsc9959_tas_clock_adjust, .update_stats = vsc9959_update_stats, + .tas_guard_bands_update = vsc9959_tas_guard_bands_update, }; static const struct felix_info felix_info_vsc9959 = { @@ -2549,7 +2670,7 @@ static const struct felix_info felix_info_vsc9959 = { .vcap_pol_max2 = 0, .num_mact_rows = 2048, .num_ports = VSC9959_NUM_PORTS, - .num_tx_queues = OCELOT_NUM_TC, + .quirks = FELIX_MAC_QUIRKS, .quirk_no_xtr_irq = true, .ptp_caps = &vsc9959_ptp_caps, .mdio_bus_alloc = vsc9959_mdio_bus_alloc, @@ -2557,102 +2678,36 @@ static const struct felix_info felix_info_vsc9959 = { .port_modes = vsc9959_port_modes, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, - .tas_guard_bands_update = vsc9959_tas_guard_bands_update, + .request_irq = vsc9959_request_irq, }; -static irqreturn_t felix_irq_handler(int irq, void *data) -{ - struct ocelot *ocelot = (struct ocelot *)data; - - /* The INTB interrupt is used for both PTP TX timestamp interrupt - * and preemption status change interrupt on each port. - * - * - Get txtstamp if have - * - TODO: handle preemption. Without handling it, driver may get - * interrupt storm. - */ - - ocelot_get_txtstamp(ocelot); - - return IRQ_HANDLED; -} - static int felix_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct dsa_switch *ds; - struct ocelot *ocelot; - struct felix *felix; + struct device *dev = &pdev->dev; + resource_size_t switch_base; int err; - if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) { - dev_info(&pdev->dev, "device is disabled, skipping\n"); - return -ENODEV; - } - err = pci_enable_device(pdev); if (err) { - dev_err(&pdev->dev, "device enable failed\n"); - goto err_pci_enable; - } - - felix = kzalloc(sizeof(struct felix), GFP_KERNEL); - if (!felix) { - err = -ENOMEM; - dev_err(&pdev->dev, "Failed to allocate driver memory\n"); - goto err_alloc_felix; + dev_err(dev, "device enable failed: %pe\n", ERR_PTR(err)); + return err; } - pci_set_drvdata(pdev, felix); - ocelot = &felix->ocelot; - ocelot->dev = &pdev->dev; - ocelot->num_flooding_pgids = OCELOT_NUM_TC; - felix->info = &felix_info_vsc9959; - felix->switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR); - pci_set_master(pdev); - err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL, - &felix_irq_handler, IRQF_ONESHOT, - "felix-intb", ocelot); - if (err) { - dev_err(&pdev->dev, "Failed to request irq\n"); - goto err_alloc_irq; - } - - ocelot->ptp = 1; + switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR); - ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL); - if (!ds) { - err = -ENOMEM; - dev_err(&pdev->dev, "Failed to allocate DSA switch\n"); - goto err_alloc_ds; - } - - 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; - felix->tag_proto = DSA_TAG_PROTO_OCELOT; - - err = dsa_register_switch(ds); - if (err) { - dev_err_probe(&pdev->dev, err, "Failed to register DSA switch\n"); - goto err_register_ds; - } + err = felix_register_switch(dev, switch_base, OCELOT_NUM_TC, + true, true, DSA_TAG_PROTO_OCELOT, + &felix_info_vsc9959); + if (err) + goto out_disable; return 0; -err_register_ds: - kfree(ds); -err_alloc_ds: -err_alloc_irq: - kfree(felix); -err_alloc_felix: +out_disable: pci_disable_device(pdev); -err_pci_enable: return err; } @@ -2665,9 +2720,6 @@ static void felix_pci_remove(struct pci_dev *pdev) dsa_unregister_switch(felix->ds); - kfree(felix->ds); - kfree(felix); - pci_disable_device(pdev); } |
