diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-05 20:13:21 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-05 20:13:21 -0700 |
commit | 47ec5303d73ea344e84f46660fff693c57641386 (patch) | |
tree | a2252debab749de29620c43285295d60c4741119 /drivers/net/dsa | |
parent | 8186749621ed6b8fc42644c399e8c755a2b6f630 (diff) | |
parent | c1055b76ad00aed0e8b79417080f212d736246b6 (diff) | |
download | linux-47ec5303d73ea344e84f46660fff693c57641386.tar.gz linux-47ec5303d73ea344e84f46660fff693c57641386.tar.bz2 linux-47ec5303d73ea344e84f46660fff693c57641386.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller:
1) Support 6Ghz band in ath11k driver, from Rajkumar Manoharan.
2) Support UDP segmentation in code TSO code, from Eric Dumazet.
3) Allow flashing different flash images in cxgb4 driver, from Vishal
Kulkarni.
4) Add drop frames counter and flow status to tc flower offloading,
from Po Liu.
5) Support n-tuple filters in cxgb4, from Vishal Kulkarni.
6) Various new indirect call avoidance, from Eric Dumazet and Brian
Vazquez.
7) Fix BPF verifier failures on 32-bit pointer arithmetic, from
Yonghong Song.
8) Support querying and setting hardware address of a port function via
devlink, use this in mlx5, from Parav Pandit.
9) Support hw ipsec offload on bonding slaves, from Jarod Wilson.
10) Switch qca8k driver over to phylink, from Jonathan McDowell.
11) In bpftool, show list of processes holding BPF FD references to
maps, programs, links, and btf objects. From Andrii Nakryiko.
12) Several conversions over to generic power management, from Vaibhav
Gupta.
13) Add support for SO_KEEPALIVE et al. to bpf_setsockopt(), from Dmitry
Yakunin.
14) Various https url conversions, from Alexander A. Klimov.
15) Timestamping and PHC support for mscc PHY driver, from Antoine
Tenart.
16) Support bpf iterating over tcp and udp sockets, from Yonghong Song.
17) Support 5GBASE-T i40e NICs, from Aleksandr Loktionov.
18) Add kTLS RX HW offload support to mlx5e, from Tariq Toukan.
19) Fix the ->ndo_start_xmit() return type to be netdev_tx_t in several
drivers. From Luc Van Oostenryck.
20) XDP support for xen-netfront, from Denis Kirjanov.
21) Support receive buffer autotuning in MPTCP, from Florian Westphal.
22) Support EF100 chip in sfc driver, from Edward Cree.
23) Add XDP support to mvpp2 driver, from Matteo Croce.
24) Support MPTCP in sock_diag, from Paolo Abeni.
25) Commonize UDP tunnel offloading code by creating udp_tunnel_nic
infrastructure, from Jakub Kicinski.
26) Several pci_ --> dma_ API conversions, from Christophe JAILLET.
27) Add FLOW_ACTION_POLICE support to mlxsw, from Ido Schimmel.
28) Add SK_LOOKUP bpf program type, from Jakub Sitnicki.
29) Refactor a lot of networking socket option handling code in order to
avoid set_fs() calls, from Christoph Hellwig.
30) Add rfc4884 support to icmp code, from Willem de Bruijn.
31) Support TBF offload in dpaa2-eth driver, from Ioana Ciornei.
32) Support XDP_REDIRECT in qede driver, from Alexander Lobakin.
33) Support PCI relaxed ordering in mlx5 driver, from Aya Levin.
34) Support TCP syncookies in MPTCP, from Flowian Westphal.
35) Fix several tricky cases of PMTU handling wrt. briding, from Stefano
Brivio.
* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2056 commits)
net: thunderx: initialize VF's mailbox mutex before first usage
usb: hso: remove bogus check for EINPROGRESS
usb: hso: no complaint about kmalloc failure
hso: fix bailout in error case of probe
ip_tunnel_core: Fix build for archs without _HAVE_ARCH_IPV6_CSUM
selftests/net: relax cpu affinity requirement in msg_zerocopy test
mptcp: be careful on subflow creation
selftests: rtnetlink: make kci_test_encap() return sub-test result
selftests: rtnetlink: correct the final return value for the test
net: dsa: sja1105: use detected device id instead of DT one on mismatch
tipc: set ub->ifindex for local ipv6 address
ipv6: add ipv6_dev_find()
net: openvswitch: silence suspicious RCU usage warning
Revert "vxlan: fix tos value before xmit"
ptp: only allow phase values lower than 1 period
farsync: switch from 'pci_' to 'dma_' API
wan: wanxl: switch from 'pci_' to 'dma_' API
hv_netvsc: do not use VF device if link is down
dpaa2-eth: Fix passing zero to 'PTR_ERR' warning
net: macb: Properly handle phylink on at91sam9x
...
Diffstat (limited to 'drivers/net/dsa')
42 files changed, 2600 insertions, 938 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index d0024cb30a7b..468b3c4273c5 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -70,6 +70,7 @@ config NET_DSA_QCA8K config NET_DSA_REALTEK_SMI tristate "Realtek SMI Ethernet switch family support" depends on NET_DSA + select NET_DSA_TAG_RTL4_A select FIXED_PHY select IRQ_DOMAIN select REALTEK_PHY diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 1df05841ab6b..6500179c2ca2 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1037,7 +1037,8 @@ static void b53_force_link(struct b53_device *dev, int port, int link) } static void b53_force_port_config(struct b53_device *dev, int port, - int speed, int duplex, int pause) + int speed, int duplex, + bool tx_pause, bool rx_pause) { u8 reg, val, off; @@ -1075,9 +1076,9 @@ static void b53_force_port_config(struct b53_device *dev, int port, return; } - if (pause & MLO_PAUSE_RX) + if (rx_pause) reg |= PORT_OVERRIDE_RX_FLOW; - if (pause & MLO_PAUSE_TX) + if (tx_pause) reg |= PORT_OVERRIDE_TX_FLOW; b53_write8(dev, B53_CTRL_PAGE, off, reg); @@ -1089,22 +1090,24 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, struct b53_device *dev = ds->priv; struct ethtool_eee *p = &dev->ports[port].eee; u8 rgmii_ctrl = 0, reg = 0, off; - int pause = 0; + bool tx_pause = false; + bool rx_pause = false; if (!phy_is_pseudo_fixed_link(phydev)) return; /* Enable flow control on BCM5301x's CPU port */ if (is5301x(dev) && port == dev->cpu_port) - pause = MLO_PAUSE_TXRX_MASK; + tx_pause = rx_pause = true; if (phydev->pause) { if (phydev->asym_pause) - pause |= MLO_PAUSE_TX; - pause |= MLO_PAUSE_RX; + tx_pause = true; + rx_pause = true; } - b53_force_port_config(dev, port, phydev->speed, phydev->duplex, pause); + b53_force_port_config(dev, port, phydev->speed, phydev->duplex, + tx_pause, rx_pause); b53_force_link(dev, port, phydev->link); if (is531x5(dev) && phy_interface_is_rgmii(phydev)) { @@ -1166,7 +1169,7 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, } else if (is5301x(dev)) { if (port != dev->cpu_port) { b53_force_port_config(dev, dev->cpu_port, 2000, - DUPLEX_FULL, MLO_PAUSE_TXRX_MASK); + DUPLEX_FULL, true, true); b53_force_link(dev, dev->cpu_port, 1); } } @@ -1251,15 +1254,9 @@ void b53_phylink_mac_config(struct dsa_switch *ds, int port, { struct b53_device *dev = ds->priv; - if (mode == MLO_AN_PHY) + if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) return; - if (mode == MLO_AN_FIXED) { - b53_force_port_config(dev, port, state->speed, - state->duplex, state->pause); - return; - } - if ((phy_interface_mode_is_8023z(state->interface) || state->interface == PHY_INTERFACE_MODE_SGMII) && dev->ops->serdes_config) @@ -1309,6 +1306,8 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, return; if (mode == MLO_AN_FIXED) { + b53_force_port_config(dev, port, speed, duplex, + tx_pause, rx_pause); b53_force_link(dev, port, true); return; } diff --git a/drivers/net/dsa/b53/b53_spi.c b/drivers/net/dsa/b53/b53_spi.c index f89f5308a99b..7abec8dab8ba 100644 --- a/drivers/net/dsa/b53/b53_spi.c +++ b/drivers/net/dsa/b53/b53_spi.c @@ -145,42 +145,52 @@ static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) { - int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2); + __le16 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 2); if (!ret) - *val = le16_to_cpu(*val); + *val = le16_to_cpu(value); return ret; } static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) { - int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4); + __le32 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 4); if (!ret) - *val = le32_to_cpu(*val); + *val = le32_to_cpu(value); return ret; } static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) { + __le64 value; int ret; *val = 0; - ret = b53_spi_read(dev, page, reg, (u8 *)val, 6); + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 6); if (!ret) - *val = le64_to_cpu(*val); + *val = le64_to_cpu(value); return ret; } static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) { - int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8); + __le64 value; + int ret; + + ret = b53_spi_read(dev, page, reg, (u8 *)&value, 8); if (!ret) - *val = le64_to_cpu(*val); + *val = le64_to_cpu(value); return ret; } diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 946e41f020a5..bafddb35f3a9 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -558,16 +558,11 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); u32 id_mode_dis = 0, port_mode; - u32 reg, offset; + u32 reg; if (port == core_readl(priv, CORE_IMP0_PRT_ID)) return; - if (priv->type == BCM7445_DEVICE_ID) - offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); - else - offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); - switch (state->interface) { case PHY_INTERFACE_MODE_RGMII: id_mode_dis = 1; @@ -582,8 +577,8 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, port_mode = EXT_REVMII; break; default: - /* all other PHYs: internal and MoCA */ - goto force_link; + /* Nothing required for all other PHYs: internal and MoCA */ + return; } /* Clear id_mode_dis bit, and the existing port mode, let @@ -592,38 +587,12 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); reg &= ~ID_MODE_DIS; reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); - reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); reg |= port_mode; if (id_mode_dis) reg |= ID_MODE_DIS; - if (state->pause & MLO_PAUSE_TXRX_MASK) { - if (state->pause & MLO_PAUSE_TX) - reg |= TX_PAUSE_EN; - reg |= RX_PAUSE_EN; - } - reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); - -force_link: - /* Force link settings detected from the PHY */ - reg = SW_OVERRIDE; - switch (state->speed) { - case SPEED_1000: - reg |= SPDSTS_1000 << SPEED_SHIFT; - break; - case SPEED_100: - reg |= SPDSTS_100 << SPEED_SHIFT; - break; - } - - if (state->link) - reg |= LINK_STS; - if (state->duplex == DUPLEX_FULL) - reg |= DUPLX_MODE; - - core_writel(priv, reg, offset); } static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port, @@ -650,6 +619,20 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { + struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + u32 reg, offset; + + if (port != core_readl(priv, CORE_IMP0_PRT_ID)) { + if (priv->type == BCM7445_DEVICE_ID) + offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); + else + offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); + + reg = core_readl(priv, offset); + reg &= ~LINK_STS; + core_writel(priv, reg, offset); + } + bcm_sf2_sw_mac_link_set(ds, port, interface, false); } @@ -662,9 +645,47 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->dev->ports[port].eee; + u32 reg, offset; bcm_sf2_sw_mac_link_set(ds, port, interface, true); + if (port != core_readl(priv, CORE_IMP0_PRT_ID)) { + if (priv->type == BCM7445_DEVICE_ID) + offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); + else + offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); + + if (interface == PHY_INTERFACE_MODE_RGMII || + interface == PHY_INTERFACE_MODE_RGMII_TXID || + interface == PHY_INTERFACE_MODE_MII || + interface == PHY_INTERFACE_MODE_REVMII) { + reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); + reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); + + if (tx_pause) + reg |= TX_PAUSE_EN; + if (rx_pause) + reg |= RX_PAUSE_EN; + + reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); + } + + reg = SW_OVERRIDE | LINK_STS; + switch (speed) { + case SPEED_1000: + reg |= SPDSTS_1000 << SPEED_SHIFT; + break; + case SPEED_100: + reg |= SPDSTS_100 << SPEED_SHIFT; + break; + } + + if (duplex == DUPLEX_FULL) + reg |= DUPLX_MODE; + + core_writel(priv, reg, offset); + } + if (mode == MLO_AN_PHY && phydev) p->eee_enabled = b53_eee_init(ds, port, phydev); } diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index f707edc641cf..d82cee5d9202 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -128,12 +128,12 @@ static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout) return count; } -static inline u32 udf_upper_bits(unsigned int num_udf) +static inline u32 udf_upper_bits(int num_udf) { return GENMASK(num_udf - 1, 0) >> (UDFS_PER_SLICE - 1); } -static inline u32 udf_lower_bits(unsigned int num_udf) +static inline u32 udf_lower_bits(int num_udf) { return (u8)GENMASK(num_udf - 1, 0); } @@ -348,8 +348,8 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, unsigned int queue_num, struct ethtool_rx_flow_spec *fs) { + __be16 vlan_tci = 0, vlan_m_tci = htons(0xffff); struct ethtool_rx_flow_spec_input input = {}; - __be16 vlan_tci = 0 , vlan_m_tci = 0xffff; const struct cfp_udf_layout *layout; unsigned int slice_num, rule_index; struct ethtool_rx_flow_rule *flow; @@ -629,8 +629,8 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, unsigned int queue_num, struct ethtool_rx_flow_spec *fs) { + __be16 vlan_tci = 0, vlan_m_tci = htons(0xffff); struct ethtool_rx_flow_spec_input input = {}; - __be16 vlan_tci = 0, vlan_m_tci = 0xffff; unsigned int slice_num, rule_index[2]; const struct cfp_udf_layout *layout; struct ethtool_rx_flow_rule *flow; diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 400207c5c7de..eb600b3dbf26 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -14,28 +14,11 @@ #include <linux/workqueue.h> #include <linux/module.h> #include <linux/if_bridge.h> +#include <linux/dsa/loop.h> #include <net/dsa.h> #include "dsa_loop.h" -struct dsa_loop_vlan { - u16 members; - u16 untagged; -}; - -struct dsa_loop_mib_entry { - char name[ETH_GSTRING_LEN]; - unsigned long val; -}; - -enum dsa_loop_mib_counters { - DSA_LOOP_PHY_READ_OK, - DSA_LOOP_PHY_READ_ERR, - DSA_LOOP_PHY_WRITE_OK, - DSA_LOOP_PHY_WRITE_ERR, - __DSA_LOOP_CNT_MAX, -}; - static struct dsa_loop_mib_entry dsa_loop_mibs[] = { [DSA_LOOP_PHY_READ_OK] = { "phy_read_ok", }, [DSA_LOOP_PHY_READ_ERR] = { "phy_read_err", }, @@ -43,21 +26,6 @@ static struct dsa_loop_mib_entry dsa_loop_mibs[] = { [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", }, }; -struct dsa_loop_port { - struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX]; -}; - -#define DSA_LOOP_VLANS 5 - -struct dsa_loop_priv { - struct mii_bus *bus; - unsigned int port_base; - struct dsa_loop_vlan vlans[DSA_LOOP_VLANS]; - struct net_device *netdev; - struct dsa_loop_port ports[DSA_MAX_PORTS]; - u16 pvid; -}; - static struct phy_device *phydevs[PHY_MAX_ADDR]; static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, @@ -191,7 +159,7 @@ dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); - if (vlan->vid_end > DSA_LOOP_VLANS) + if (vlan->vid_end > ARRAY_SIZE(ps->vlans)) return -ERANGE; return 0; @@ -224,7 +192,7 @@ static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, } if (pvid) - ps->pvid = vid; + ps->ports[port].pvid = vid; } static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, @@ -234,7 +202,7 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; - u16 vid, pvid = ps->pvid; + u16 vid, pvid = ps->ports[port].pvid; /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); @@ -252,11 +220,26 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", __func__, port, vid, untagged ? "un" : "", pvid); } - ps->pvid = pvid; + ps->ports[port].pvid = pvid; + + return 0; +} + +static int dsa_loop_port_change_mtu(struct dsa_switch *ds, int port, + int new_mtu) +{ + struct dsa_loop_priv *priv = ds->priv; + + priv->ports[port].mtu = new_mtu; return 0; } +static int dsa_loop_port_max_mtu(struct dsa_switch *ds, int port) +{ + return ETH_MAX_MTU; +} + static const struct dsa_switch_ops dsa_loop_driver = { .get_tag_protocol = dsa_loop_get_protocol, .setup = dsa_loop_setup, @@ -273,6 +256,8 @@ static const struct dsa_switch_ops dsa_loop_driver = { .port_vlan_prepare = dsa_loop_port_vlan_prepare, .port_vlan_add = dsa_loop_port_vlan_add, .port_vlan_del = dsa_loop_port_vlan_del, + .port_change_mtu = dsa_loop_port_change_mtu, + .port_max_mtu = dsa_loop_port_max_mtu, }; static int dsa_loop_drv_probe(struct mdio_device *mdiodev) @@ -280,19 +265,17 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev) struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data; struct dsa_loop_priv *ps; struct dsa_switch *ds; + int ret; if (!pdata) return -ENODEV; - dev_info(&mdiodev->dev, "%s: 0x%0x\n", - pdata->name, pdata->enabled_ports); - ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; ds->dev = &mdiodev->dev; - ds->num_ports = DSA_MAX_PORTS; + ds->num_ports = DSA_LOOP_NUM_PORTS; ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); if (!ps) @@ -311,7 +294,12 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev) dev_set_drvdata(&mdiodev->dev, ds); - return dsa_register_switch(ds); + ret = dsa_register_switch(ds); + if (!ret) + dev_info(&mdiodev->dev, "%s: 0x%0x\n", + pdata->name, pdata->enabled_ports); + + return ret; } static void dsa_loop_drv_remove(struct mdio_device *mdiodev) diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index cc17a44dd3a8..aa1142d6a9f5 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1042,7 +1042,7 @@ static void lan9303_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct lan9303 *chip = ds->priv; - int ctl, res; + int ctl; if (!phy_is_pseudo_fixed_link(phydev)) return; @@ -1063,15 +1063,14 @@ static void lan9303_adjust_link(struct dsa_switch *ds, int port, else ctl &= ~BMCR_FULLDPLX; - res = lan9303_phy_write(ds, port, MII_BMCR, ctl); + lan9303_phy_write(ds, port, MII_BMCR, ctl); if (port == chip->phy_addr_base) { /* Virtual Phy: Remove Turbo 200Mbit mode */ lan9303_read(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, &ctl); ctl &= ~LAN9303_VIRT_SPECIAL_TURBO; - res = regmap_write(chip->regmap, - LAN9303_VIRT_SPECIAL_CTRL, ctl); + regmap_write(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, ctl); } } diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 7c17b0f705ec..8f1d15ea15d9 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -731,15 +731,6 @@ static void ksz8795_port_stp_state_set(struct dsa_switch *ds, int port, ksz_pwrite8(dev, port, P_STP_CTRL, data); p->stp_state = state; - if (data & PORT_RX_ENABLE) - dev->rx_ports |= BIT(port); - else - dev->rx_ports &= ~BIT(port); - if (data & PORT_TX_ENABLE) - dev->tx_ports |= BIT(port); - else - dev->tx_ports &= ~BIT(port); - /* Port membership may share register with STP state. */ if (member >= 0 && member != p->member) ksz8795_cfg_port_member(dev, port, (u8)member); @@ -976,15 +967,8 @@ static void ksz8795_port_setup(struct ksz_device *dev, int port, bool cpu_port) p->phydev.duplex = 1; member = dev->port_mask; - dev->on_ports = dev->host_mask; - dev->live_ports = dev->host_mask; } else { member = dev->host_mask | p->vid_member; - dev->on_ports |= BIT(port); - - /* Link was detected before port is enabled. */ - if (p->phydev.link) - dev->live_ports |= BIT(port); } ksz8795_cfg_port_member(dev, port, member); } @@ -1111,9 +1095,8 @@ static const struct dsa_switch_ops ksz8795_switch_ops = { .setup = ksz8795_setup, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, - .adjust_link = ksz_adjust_link, + .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, - .port_disable = ksz_disable_port, .get_strings = ksz8795_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, .get_sset_count = ksz_sset_count, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 4a9239b2c2e4..dc999406ce86 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -452,15 +452,6 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, ksz_pwrite8(dev, port, P_STP_CTRL, data); p->stp_state = state; mutex_lock(&dev->dev_mutex); - if (data & PORT_RX_ENABLE) - dev->rx_ports |= (1 << port); - else - dev->rx_ports &= ~(1 << port); - if (data & PORT_TX_ENABLE) - dev->tx_ports |= (1 << port); - else - dev->tx_ports &= ~(1 << port); - /* Port membership may share register with STP state. */ if (member >= 0 && member != p->member) ksz9477_cfg_port_member(dev, port, (u8)member); @@ -1251,18 +1242,10 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) p->phydev.duplex = 1; } mutex_lock(&dev->dev_mutex); - if (cpu_port) { + if (cpu_port) member = dev->port_mask; - dev->on_ports = dev->host_mask; - dev->live_ports = dev->host_mask; - } else { + else member = dev->host_mask | p->vid_member; - dev->on_ports |= (1 << port); - - /* Link was detected before port is enabled. */ - if (p->phydev.link) - dev->live_ports |= (1 << port); - } mutex_unlock(&dev->dev_mutex); ksz9477_cfg_port_member(dev, port, member); @@ -1382,9 +1365,8 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .setup = ksz9477_setup, .phy_read = ksz9477_phy_read16, .phy_write = ksz9477_phy_write16, - .adjust_link = ksz_adjust_link, + .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, - .port_disable = ksz_disable_port, .get_strings = ksz9477_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, .get_sset_count = ksz_sset_count, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 7b6c0dce7536..8d53b12d40a8 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -135,26 +135,17 @@ int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) } EXPORT_SYMBOL_GPL(ksz_phy_write16); -void ksz_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) { struct ksz_device *dev = ds->priv; struct ksz_port *p = &dev->ports[port]; /* Read all MIB counters when the link is going down. */ - if (!phydev->link) { - p->read = true; - schedule_delayed_work(&dev->mib_read, 0); - } - mutex_lock(&dev->dev_mutex); - if (!phydev->link) - dev->live_ports &= ~(1 << port); - else - /* Remember which port is connected and active. */ - dev->live_ports |= (1 << port) & dev->on_ports; - mutex_unlock(&dev->dev_mutex); + p->read = true; + schedule_delayed_work(&dev->mib_read, 0); } -EXPORT_SYMBOL_GPL(ksz_adjust_link); +EXPORT_SYMBOL_GPL(ksz_mac_link_down); int ksz_sset_count(struct dsa_switch *ds, int port, int sset) { @@ -367,22 +358,6 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) } EXPORT_SYMBOL_GPL(ksz_enable_port); -void ksz_disable_port(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - if (!dsa_is_user_port(ds, port)) - return; - - dev->on_ports &= ~(1 << port); - dev->live_ports &= ~(1 << port); - - /* port_stp_state_set() will be called after to disable the port so - * there is no need to do anything. - */ -} -EXPORT_SYMBOL_GPL(ksz_disable_port); - struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) { struct dsa_switch *ds; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 7d11dd32ec0d..206838160f49 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -84,10 +84,6 @@ struct ksz_device { unsigned long mib_read_interval; u16 br_member; u16 member; - u16 live_ports; - u16 on_ports; /* ports enabled by DSA */ - u16 rx_ports; - u16 tx_ports; u16 mirror_rx; u16 mirror_tx; u32 features; /* chip specific features */ @@ -157,8 +153,8 @@ void ksz_init_mib_timer(struct ksz_device *dev); int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg); int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val); -void ksz_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev); +void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface); int ksz_sset_count(struct dsa_switch *ds, int port, int sset); void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf); int ksz_port_bridge_join(struct dsa_switch *ds, int port, @@ -177,7 +173,6 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port, int ksz_port_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); -void ksz_disable_port(struct dsa_switch *ds, int port); /* Common register access functions */ diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index fee16c947c2e..7a71c9902e73 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1767,7 +1767,7 @@ static int mv88e6xxx_policy_insert(struct mv88e6xxx_chip *chip, int port, } if ((fs->flow_type & FLOW_EXT) && fs->m_ext.vlan_tci) { - if (fs->m_ext.vlan_tci != 0xffff) + if (fs->m_ext.vlan_tci != htons(0xffff)) return -EOPNOTSUPP; vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; } @@ -2709,6 +2709,35 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN, 0); } +static int mv88e6xxx_get_max_mtu(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + if (chip->info->ops->port_set_jumbo_size) + return 10240; + else if (chip->info->ops->set_max_frame_size) + return 1632; + return 1522; +} + +static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int ret = 0; + + mv88e6xxx_reg_lock(chip); + if (chip->info->ops->port_set_jumbo_size) + ret = chip->info->ops->port_set_jumbo_size(chip, port, new_mtu); + else if (chip->info->ops->set_max_frame_size) + ret = chip->info->ops->set_max_frame_size(chip, new_mtu); + else + if (new_mtu > 1522) + ret = -EINVAL; + mv88e6xxx_reg_unlock(chip); + + return ret; +} + static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, struct phy_device *phydev) { @@ -3441,6 +3470,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3469,6 +3499,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -3485,7 +3516,6 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, - .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, @@ -3507,6 +3537,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3541,6 +3572,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3930,6 +3962,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3950,6 +3983,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, @@ -4008,6 +4042,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, @@ -5541,6 +5576,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_sset_count = mv88e6xxx_get_sset_count, .port_enable = mv88e6xxx_port_enable, .port_disable = mv88e6xxx_port_disable, + .port_max_mtu = mv88e6xxx_get_max_mtu, + .port_change_mtu = mv88e6xxx_change_mtu, .get_mac_eee = mv88e6xxx_get_mac_eee, .set_mac_eee = mv88e6xxx_set_mac_eee, .get_eeprom_len = mv88e6xxx_get_eeprom_len, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 6476524e8239..823ae89e5fca 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -167,7 +167,7 @@ struct mv88e6xxx_irq { u16 masked; struct irq_chip chip; struct irq_domain *domain; - unsigned int nirqs; + int nirqs; }; /* state flags for mv88e6xxx_port_hwtstamp::state */ @@ -553,6 +553,9 @@ struct mv88e6xxx_ops { void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, unsigned long *mask, struct phylink_link_state *state); + + /* Max Frame Size */ + int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); }; struct mv88e6xxx_irq_ops { @@ -655,7 +658,7 @@ static inline unsigned int mv88e6xxx_num_ports(struct mv88e6xxx_chip *chip) static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip) { - return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); + return GENMASK((s32)mv88e6xxx_num_ports(chip) - 1, 0); } static inline unsigned int mv88e6xxx_num_gpio(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index ca3a7a7a73c3..f62aa83ca08d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -196,6 +196,23 @@ int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip) return mv88e6185_g1_wait_ppu_disabled(chip); } +int mv88e6185_g1_set_max_frame_size(struct mv88e6xxx_chip *chip, int mtu) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val); + if (err) + return err; + + val &= ~MV88E6185_G1_CTL1_MAX_FRAME_1632; + + if (mtu > 1518) + val |= MV88E6185_G1_CTL1_MAX_FRAME_1632; + + return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val); +} + /* Offset 0x10: IP-PRI Mapping Register 0 * Offset 0x11: IP-PRI Mapping Register 1 * Offset 0x12: IP-PRI Mapping Register 2 diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 5324c6f4ae90..1e3546f8b072 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -282,6 +282,8 @@ int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); +int mv88e6185_g1_set_max_frame_size(struct mv88e6xxx_chip *chip, int mtu); + int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 8fd483020c5b..75b227d0f73b 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -876,19 +876,18 @@ static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) { - int err; u16 reg; mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, MV88E6390_G2_WDOG_CTL_PTR_EVENT); - err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, ®); + mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, ®); dev_info(chip->dev, "Watchdog event: 0x%04x", reg & MV88E6390_G2_WDOG_CTL_DATA_MASK); mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, MV88E6390_G2_WDOG_CTL_PTR_HISTORY); - err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, ®); + mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, ®); dev_info(chip->dev, "Watchdog history: 0x%04x", reg & MV88E6390_G2_WDOG_CTL_DATA_MASK); diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c index 33b7b9570d29..7c2c67405322 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c @@ -44,7 +44,8 @@ static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg, /** * mv88e6xxx_g2_scratch_gpio_get_bit - get a bit * @chip: chip private data - * @nr: bit index + * @base_reg: base of scratch bits + * @offset: index of bit within the register * @set: is bit set? */ static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip, @@ -68,8 +69,9 @@ static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip, /** * mv88e6xxx_g2_scratch_gpio_set_bit - set (or clear) a bit * @chip: chip private data - * @nr: bit index - * @set: set if true, clear if false + * @base_reg: base of scratch bits + * @offset: index of bit within the register + * @set: should this bit be set? * * Helper function for dealing with the direction and data registers. */ @@ -165,6 +167,7 @@ static int mv88e6352_g2_scratch_gpio_get_dir(struct mv88e6xxx_chip *chip, * mv88e6352_g2_scratch_gpio_set_dir - set direction of gpio pin * @chip: chip private data * @pin: gpio index + * @input: should the gpio be an input, or an output? */ static int mv88e6352_g2_scratch_gpio_set_dir(struct mv88e6xxx_chip *chip, unsigned int pin, bool input) diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index a5b7cca03d09..f121619d81fe 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -4,11 +4,16 @@ config NET_DSA_MSCC_FELIX depends on NET_DSA && PCI depends on NET_VENDOR_MICROSEMI depends on NET_VENDOR_FREESCALE - select MSCC_OCELOT_SWITCH + depends on HAS_IOMEM + select MSCC_OCELOT_SWITCH_LIB select NET_DSA_TAG_OCELOT select FSL_ENETC_MDIO help - This driver supports the VSC9959 network switch, which is a member of - the Vitesse / Microsemi / Microchip Ocelot family of switching cores. - It is embedded as a PCIe function of the NXP LS1028A ENETC integrated - endpoint. + This driver supports network switches from the the Vitesse / + Microsemi / Microchip Ocelot family of switching cores that are + connected to their host CPU via Ethernet. + The following switches are supported: + - VSC9959 (Felix): embedded as a PCIe function of the NXP LS1028A + ENETC integrated endpoint. + - VSC9953 (Seville): embedded as a platform device on the + NXP T1040 SoC. diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile index 37ad403e0b2a..ec57a5a12330 100644 --- a/drivers/net/dsa/ocelot/Makefile +++ b/drivers/net/dsa/ocelot/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o mscc_felix-objs := \ felix.o \ - felix_vsc9959.o + felix_vsc9959.o \ + seville_vsc9953.o diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 66648986e6e3..c69d9592a2b7 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2019 NXP Semiconductors + * + * This is an umbrella module for all network switches that are + * register-compatible with Ocelot and that perform I/O to their host CPU + * through an NPI (Node Processor Interface) Ethernet port. */ #include <uapi/linux/if_bridge.h> #include <soc/mscc/ocelot_vcap.h> @@ -9,6 +13,7 @@ #include <soc/mscc/ocelot_ana.h> #include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot.h> +#include <linux/platform_device.h> #include <linux/packing.h> #include <linux/module.h> #include <linux/of_net.h> @@ -59,6 +64,29 @@ static int felix_fdb_del(struct dsa_switch *ds, int port, return ocelot_fdb_del(ocelot, port, addr, vid); } +/* This callback needs to be present */ +static int felix_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + return 0; +} + +static void felix_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ocelot *ocelot = ds->priv; + + ocelot_port_mdb_add(ocelot, port, mdb); +} + +static int felix_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_mdb_del(ocelot, port, mdb); +} + static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, u8 state) { @@ -162,35 +190,10 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port, struct phylink_link_state *state) { struct ocelot *ocelot = ds->priv; - struct ocelot_port *ocelot_port = ocelot->ports[port]; - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != ocelot_port->phy_mode) { - bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); - return; - } - - /* No half-duplex. */ - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 1000baseT_Full); - - if (state->interface == PHY_INTERFACE_MODE_INTERNAL || - state->interface == PHY_INTERFACE_MODE_2500BASEX || - state->interface == PHY_INTERFACE_MODE_USXGMII) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } + struct felix *felix = ocelot_to_felix(ocelot); - bitmap_and(supported, supported, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); + if (felix->info->phylink_validate) + felix->info->phylink_validate(ocelot, port, supported, state); } static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port, @@ -210,50 +213,10 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port, const struct phylink_link_state *state) { struct ocelot *ocelot = ds->priv; - struct ocelot_port *ocelot_port = ocelot->ports[port]; struct felix *felix = ocelot_to_felix(ocelot); - u32 mac_fc_cfg; - - /* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and - * PORT_RST bits in CLOCK_CFG - */ - ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(state->speed), - DEV_CLOCK_CFG); - - /* Flow control. Link speed is only used here to evaluate the time - * specification in incoming pause frames. - */ - mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(state->speed); - /* handle Rx pause in all cases, with 2500base-X this is used for rate - * adaptation. - */ - mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; - - if (state->pause & MLO_PAUSE_TX) - mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | - SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | - SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | - SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; - ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); - - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, 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) -{ - struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); - - if (felix->info->pcs_an_restart) - felix->info->pcs_an_restart(ocelot, port); + if (felix->info->pcs_config) + felix->info->pcs_config(ocelot, port, link_an_mode, state); } static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, @@ -264,8 +227,7 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, struct ocelot_port *ocelot_port = ocelot->ports[port]; ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); - ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, - QSYS_SWITCH_PORT_MODE, port); + ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0); } static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -277,8 +239,58 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, { struct ocelot *ocelot = ds->priv; struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct felix *felix = ocelot_to_felix(ocelot); + u32 mac_fc_cfg; + + /* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and + * PORT_RST bits in DEV_CLOCK_CFG. Note that the way this system is + * integrated is that the MAC speed is fixed and it's the PCS who is + * performing the rate adaptation, so we have to write "1000Mbps" into + * the LINK_SPEED field of DEV_CLOCK_CFG (which is also its default + * value). + */ + ocelot_port_writel(ocelot_port, + DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000), + DEV_CLOCK_CFG); + + switch (speed) { + case SPEED_10: + mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(3); + break; + case SPEED_100: + mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(2); + break; + case SPEED_1000: + case SPEED_2500: + mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(1); + break; + default: + dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n", + port, speed); + return; + } + + /* handle Rx pause in all cases, with 2500base-X this is used for rate + * adaptation. + */ + mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; + + if (tx_pause) + mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | + SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | + SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | + SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; - /* Enable MAC module */ + /* Flow control. Link speed is only used here to evaluate the time + * specification in incoming pause frames. + */ + ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); + + ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); + + /* Undo the effects of felix_phylink_mac_link_down: + * enable MAC module + */ ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG); @@ -291,10 +303,15 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, ANA_PORT_PORT_CFG, port); /* Core: Enable port for frame transfer */ - ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | - QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | - QSYS_SWITCH_PORT_MODE_PORT_ENA, - QSYS_SWITCH_PORT_MODE, port); + ocelot_fields_write(ocelot, port, + QSYS_SWITCH_PORT_MODE_PORT_ENA, 1); + + if (felix->info->pcs_link_up) + felix->info->pcs_link_up(ocelot, port, link_an_mode, interface, + speed, duplex); + + if (felix->info->port_sched_speed_set) + felix->info->port_sched_speed_set(ocelot, port, speed); } static void felix_port_qos_map_init(struct ocelot *ocelot, int port) @@ -417,7 +434,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) { struct ocelot *ocelot = &felix->ocelot; phy_interface_t *port_phy_modes; - resource_size_t switch_base; struct resource res; int port, i, err; @@ -448,9 +464,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) return err; } - switch_base = pci_resource_start(felix->pdev, - felix->info->switch_pci_bar); - for (i = 0; i < TARGET_MAX; i++) { struct regmap *target; @@ -459,8 +472,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) memcpy(&res, &felix->info->target_io_res[i], sizeof(res)); res.flags = IORESOURCE_MEM; - res.start += switch_base; - res.end += switch_base; + res.start += felix->switch_base; + res.end += felix->switch_base; target = ocelot_regmap_init(ocelot, &res); if (IS_ERR(target)) { @@ -482,7 +495,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) for (port = 0; port < num_phys_ports; port++) { struct ocelot_port *ocelot_port; - void __iomem *port_regs; + struct regmap *target; + u8 *template; ocelot_port = devm_kzalloc(ocelot->dev, sizeof(struct ocelot_port), @@ -496,21 +510,34 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) memcpy(&res, &felix->info->port_io_res[port], sizeof(res)); res.flags = IORESOURCE_MEM; - res.start += switch_base; - res.end += switch_base; + res.start += felix->switch_base; + res.end += felix->switch_base; + + target = ocelot_regmap_init(ocelot, &res); + if (IS_ERR(target)) { + dev_err(ocelot->dev, + "Failed to map memory space for port %d\n", + port); + kfree(port_phy_modes); + return PTR_ERR(target); + } - port_regs = devm_ioremap_resource(ocelot->dev, &res); - if (IS_ERR(port_regs)) { + template = devm_kzalloc(ocelot->dev, OCELOT_TAG_LEN, + GFP_KERNEL); + if (!template) { dev_err(ocelot->dev, - "failed to map registers for port %d\n", port); + "Failed to allocate memory for DSA tag\n"); kfree(port_phy_modes); - return PTR_ERR(port_regs); + return -ENOMEM; } ocelot_port->phy_mode = port_phy_modes[port]; ocelot_port->ocelot = ocelot; - ocelot_port->regs = port_regs; + ocelot_port->target = target; + ocelot_port->xmit_template = template; ocelot->ports[port] = ocelot_port; + + felix->info->xmit_template_populate(ocelot, port); } kfree(port_phy_modes); @@ -723,9 +750,7 @@ static int felix_port_policer_add(struct dsa_switch *ds, int port, struct ocelot *ocelot = ds->priv; struct ocelot_policer pol = { .rate = div_u64(policer->rate_bytes_per_sec, 1000) * 8, - .burst = div_u64(policer->rate_bytes_per_sec * - PSCHED_NS2TICKS(policer->burst), - PSCHED_TICKS_PER_SEC), + .burst = policer->burst, }; return ocelot_port_policer_add(ocelot, port, &pol); @@ -751,7 +776,7 @@ static int felix_port_setup_tc(struct dsa_switch *ds, int port, return -EOPNOTSUPP; } -static const struct dsa_switch_ops felix_switch_ops = { +const struct dsa_switch_ops felix_switch_ops = { .get_tag_protocol = felix_get_tag_protocol, .setup = felix_setup, .teardown = felix_teardown, @@ -763,7 +788,6 @@ static const struct dsa_switch_ops felix_switch_ops = { .phylink_validate = felix_phylink_validate, .phylink_mac_link_state = felix_phylink_mac_pcs_get_state, .phylink_mac_config = felix_phylink_mac_config, - .phylink_mac_an_restart = felix_phylink_mac_an_restart, .phylink_mac_link_down = felix_phylink_mac_link_down, .phylink_mac_link_up = felix_phylink_mac_link_up, .port_enable = felix_port_enable, @@ -771,6 +795,9 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_fdb_dump = felix_fdb_dump, .port_fdb_add = felix_fdb_add, .port_fdb_del = felix_fdb_del, + .port_mdb_prepare = felix_mdb_prepare, + .port_mdb_add = felix_mdb_add, + .port_mdb_del = felix_mdb_del, .port_bridge_join = felix_bridge_join, .port_bridge_leave = felix_bridge_leave, .port_stp_state_set = felix_bridge_stp_state_set, @@ -792,149 +819,28 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_setup_tc = felix_port_setup_tc, }; -static struct felix_info *felix_instance_tbl[] = { - [FELIX_INSTANCE_VSC9959] = &felix_info_vsc9959, -}; - -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) +static int __init felix_init(void) { - enum felix_instance instance = id->driver_data; - struct dsa_switch *ds; - struct ocelot *ocelot; - struct felix *felix; 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; - } - - /* set up for high or low dma */ - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (err) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "DMA configuration failed: 0x%x\n", err); - goto err_dma; - } - } - - 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; - } - - pci_set_drvdata(pdev, felix); - ocelot = &felix->ocelot; - ocelot->dev = &pdev->dev; - felix->pdev = pdev; - felix->info = felix_instance_tbl[instance]; - - 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; - - 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; + err = pci_register_driver(&felix_vsc9959_pci_driver); + if (err) + return err; - err = dsa_register_switch(ds); - if (err) { - dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err); - goto err_register_ds; - } + err = platform_driver_register(&seville_vsc9953_driver); + if (err) + return err; return 0; - -err_register_ds: - kfree(ds); -err_alloc_ds: -err_alloc_irq: -err_alloc_felix: - kfree(felix); -err_dma: - pci_disable_device(pdev); -err_pci_enable: - return err; } +module_init(felix_init); -static void felix_pci_remove(struct pci_dev *pdev) +static void __exit felix_exit(void) { - struct felix *felix; - - felix = pci_get_drvdata(pdev); - - dsa_unregister_switch(felix->ds); - - kfree(felix->ds); - kfree(felix); - - pci_disable_device(pdev); + pci_unregister_driver(&felix_vsc9959_pci_driver); + platform_driver_unregister(&seville_vsc9953_driver); } - -static struct pci_device_id felix_ids[] = { - { - /* NXP LS1028A */ - PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0xEEF0), - .driver_data = FELIX_INSTANCE_VSC9959, - }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, felix_ids); - -static struct pci_driver felix_pci_driver = { - .name = KBUILD_MODNAME, - .id_table = felix_ids, - .probe = felix_pci_probe, - .remove = felix_pci_remove, -}; - -module_pci_driver(felix_pci_driver); +module_exit(felix_exit); MODULE_DESCRIPTION("Felix Switch driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index a891736ca006..98f14621ac23 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -28,34 +28,51 @@ struct felix_info { int imdio_pci_bar; int (*mdio_bus_alloc)(struct ocelot *ocelot); void (*mdio_bus_free)(struct ocelot *ocelot); - void (*pcs_init)(struct ocelot *ocelot, int port, - unsigned int link_an_mode, - const struct phylink_link_state *state); - void (*pcs_an_restart)(struct ocelot *ocelot, int port); + void (*pcs_config)(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state); + void (*pcs_link_up)(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + phy_interface_t interface, + int speed, int duplex); void (*pcs_link_state)(struct ocelot *ocelot, int port, struct phylink_link_state *state); + void (*phylink_validate)(struct ocelot *ocelot, int port, + unsigned long *supported, + 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); + void (*xmit_template_populate)(struct ocelot *ocelot, int port); }; -extern struct felix_info felix_info_vsc9959; - -enum felix_instance { - FELIX_INSTANCE_VSC9959 = 0, -}; +extern const struct dsa_switch_ops felix_switch_ops; +extern struct pci_driver felix_vsc9959_pci_driver; +extern struct platform_driver seville_vsc9953_driver; /* DSA glue / front-end for struct ocelot */ struct felix { struct dsa_switch *ds; - struct pci_dev *pdev; - struct felix_info *info; + const struct felix_info *info; struct ocelot ocelot; struct mii_bus *imdio; struct phy_device **pcs; + resource_size_t switch_base; + resource_size_t imdio_base; }; +void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, + struct phylink_link_state *state); +void vsc9959_pcs_config(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state); +void vsc9959_pcs_link_up(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + phy_interface_t interface, + int speed, int duplex); +void vsc9959_mdio_bus_free(struct ocelot *ocelot); + #endif diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 1dd9e348152d..9b720c8ddfc3 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -8,37 +8,18 @@ #include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot.h> +#include <linux/packing.h> #include <net/pkt_sched.h> #include <linux/iopoll.h> +#include <linux/mdio.h> #include <linux/pci.h> #include "felix.h" #define VSC9959_VCAP_IS2_CNT 1024 #define VSC9959_VCAP_IS2_ENTRY_WIDTH 376 #define VSC9959_VCAP_PORT_CNT 6 - -/* TODO: should find a better place for these */ -#define USXGMII_BMCR_RESET BIT(15) -#define USXGMII_BMCR_AN_EN BIT(12) -#define USXGMII_BMCR_RST_AN BIT(9) -#define USXGMII_BMSR_LNKS(status) (((status) & GENMASK(2, 2)) >> 2) -#define USXGMII_BMSR_AN_CMPL(status) (((status) & GENMASK(5, 5)) >> 5) -#define USXGMII_ADVERTISE_LNKS(x) (((x) << 15) & BIT(15)) -#define USXGMII_ADVERTISE_FDX BIT(12) -#define USXGMII_ADVERTISE_SPEED(x) (((x) << 9) & GENMASK(11, 9)) -#define USXGMII_LPA_LNKS(lpa) ((lpa) >> 15) -#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, - USXGMII_SPEED_1000 = 2, - USXGMII_SPEED_2500 = 4, -}; - static const u32 vsc9959_ana_regmap[] = { REG(ANA_ADVLEARN, 0x0089a0), REG(ANA_VLANMASK, 0x0089a4), @@ -329,7 +310,49 @@ static const u32 vsc9959_gcb_regmap[] = { REG(GCB_SOFT_RST, 0x000004), }; -static const u32 *vsc9959_regmap[] = { +static const u32 vsc9959_dev_gmii_regmap[] = { + REG(DEV_CLOCK_CFG, 0x0), + REG(DEV_PORT_MISC, 0x4), + REG(DEV_EVENTS, 0x8), + REG(DEV_EEE_CFG, 0xc), + REG(DEV_RX_PATH_DELAY, 0x10), + REG(DEV_TX_PATH_DELAY, 0x14), + REG(DEV_PTP_PREDICT_CFG, 0x18), + REG(DEV_MAC_ENA_CFG, 0x1c), + REG(DEV_MAC_MODE_CFG, 0x20), + REG(DEV_MAC_MAXLEN_CFG, 0x24), + REG(DEV_MAC_TAGS_CFG, 0x28), + REG(DEV_MAC_ADV_CHK_CFG, 0x2c), + REG(DEV_MAC_IFG_CFG, 0x30), + REG(DEV_MAC_HDX_CFG, 0x34), + REG(DEV_MAC_DBG_CFG, 0x38), + REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c), + REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40), + REG(DEV_MAC_STICKY, 0x44), + REG_RESERVED(PCS1G_CFG), + REG_RESERVED(PCS1G_MODE_CFG), + REG_RESERVED(PCS1G_SD_CFG), + REG_RESERVED(PCS1G_ANEG_CFG), + REG_RESERVED(PCS1G_ANEG_NP_CFG), + REG_RESERVED(PCS1G_LB_CFG), + REG_RESERVED(PCS1G_DBG_CFG), + REG_RESERVED(PCS1G_CDET_CFG), + REG_RESERVED(PCS1G_ANEG_STATUS), + REG_RESERVED(PCS1G_ANEG_NP_STATUS), + REG_RESERVED(PCS1G_LINK_STATUS), + REG_RESERVED(PCS1G_LINK_DOWN_CNT), + REG_RESERVED(PCS1G_STICKY), + REG_RESERVED(PCS1G_DEBUG_STATUS), + REG_RESERVED(PCS1G_LPI_CFG), + REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT), + REG_RESERVED(PCS1G_LPI_STATUS), + REG_RESERVED(PCS1G_TSTPAT_MODE_CFG), + REG_RESERVED(PCS1G_TSTPAT_STATUS), + REG_RESERVED(DEV_PCS_FX100_CFG), + REG_RESERVED(DEV_PCS_FX100_STATUS), +}; + +static const u32 *vsc9959_regmap[TARGET_MAX] = { [ANA] = vsc9959_ana_regmap, [QS] = vsc9959_qs_regmap, [QSYS] = vsc9959_qsys_regmap, @@ -338,10 +361,11 @@ static const u32 *vsc9959_regmap[] = { [S2] = vsc9959_s2_regmap, [PTP] = vsc9959_ptp_regmap, [GCB] = vsc9959_gcb_regmap, + [DEV_GMII] = vsc9959_dev_gmii_regmap, }; /* Addresses are relative to the PCI device's base address */ -static const struct resource vsc9959_target_io_res[] = { +static const struct resource vsc9959_target_io_res[TARGET_MAX] = { [ANA] = { .start = 0x0280000, .end = 0x028ffff, @@ -426,7 +450,7 @@ static const struct resource vsc9959_imdio_res = { .name = "imdio", }; -static const struct reg_field vsc9959_regfields[] = { +static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = { [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6), [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5), [ANA_ANEVENTS_FLOOD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 30, 30), @@ -460,6 +484,20 @@ static const struct reg_field vsc9959_regfields[] = { [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 10), [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 0, 0), [GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0), + /* Replicated per number of ports (7), register size 4 per port */ + [QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 7, 4), + [QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 7, 4), + [QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 7, 4), + [QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 7, 4), + [QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 7, 4), + [QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 7, 4), + [SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 7, 4), + [SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 7, 4), + [SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 7, 4), + [SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 7, 4), + [SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 7, 4), + [SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 7, 4), + [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 7, 4), }; static const struct ocelot_stat_layout vsc9959_stats_layout[] = { @@ -557,7 +595,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = { { .offset = 0x111, .name = "drop_green_prio_7", }, }; -struct vcap_field vsc9959_vcap_is2_keys[] = { +static struct vcap_field vsc9959_vcap_is2_keys[] = { /* Common: 41 bits */ [VCAP_IS2_TYPE] = { 0, 4}, [VCAP_IS2_HK_FIRST] = { 4, 1}, @@ -637,7 +675,7 @@ struct vcap_field vsc9959_vcap_is2_keys[] = { [VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1}, }; -struct vcap_field vsc9959_vcap_is2_actions[] = { +static struct vcap_field vsc9959_vcap_is2_actions[] = { [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, @@ -728,20 +766,74 @@ static int vsc9959_reset(struct ocelot *ocelot) return 0; } -static void vsc9959_pcs_an_restart_sgmii(struct phy_device *pcs) +/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the + * device tree. If we are in MLO_AN_PHY mode, we program directly state->speed + * into the PCS, which is retrieved out-of-band over MDIO. This also has the + * benefit of working with SGMII fixed-links, like downstream switches, where + * both link partners attempt to operate as AN slaves and therefore AN never + * completes. But it also has the disadvantage that some PHY chips don't pass + * traffic if SGMII AN is enabled but not completed (acknowledged by us), so + * setting MLO_AN_INBAND is actually required for those. + */ +static void vsc9959_pcs_config_sgmii(struct phy_device *pcs, + unsigned int link_an_mode, + const struct phylink_link_state *state) { - phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART); + int bmsr, bmcr; + + /* Some PHYs like VSC8234 don't like it when AN restarts on + * their system side and they restart line side AN too, going + * into an endless link up/down loop. Don't restart PCS AN if + * link is up already. + * We do check that AN is enabled just in case this is the 1st + * call, PCS detects a carrier but AN is disabled from power on + * or by boot loader. + */ + bmcr = phy_read(pcs, MII_BMCR); + if (bmcr < 0) + return; + + bmsr = phy_read(pcs, MII_BMSR); + if (bmsr < 0) + return; + + if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS)) + return; + + /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 + * for the MAC PCS in order to acknowledge the AN. + */ + phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | + ADVERTISE_LPACK); + + phy_write(pcs, ENETC_PCS_IF_MODE, + ENETC_PCS_IF_MODE_SGMII_EN | + ENETC_PCS_IF_MODE_USE_SGMII_AN); + + /* Adjust link timer for SGMII */ + phy_write(pcs, ENETC_PCS_LINK_TIMER1, + ENETC_PCS_LINK_TIMER1_VAL); + phy_write(pcs, ENETC_PCS_LINK_TIMER2, + ENETC_PCS_LINK_TIMER2_VAL); + + phy_set_bits(pcs, MII_BMCR, BMCR_ANENABLE); } -static void vsc9959_pcs_an_restart_usxgmii(struct phy_device *pcs) +static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs, + unsigned int link_an_mode, + const struct phylink_link_state *state) { - phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR, - USXGMII_BMCR_RESET | - USXGMII_BMCR_AN_EN | - USXGMII_BMCR_RST_AN); + /* Configure device ability for the USXGMII Replicator */ + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, + MDIO_USXGMII_2500FULL | + MDIO_USXGMII_LINK | + ADVERTISE_SGMII | + ADVERTISE_LPACK); } -static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port) +void vsc9959_pcs_config(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state) { struct felix *felix = ocelot_to_felix(ocelot); struct phy_device *pcs = felix->pcs[port]; @@ -749,107 +841,76 @@ static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port) if (!pcs) return; + /* The PCS does not implement the BMSR register fully, so capability + * detection via genphy_read_abilities does not work. Since we can get + * the PHY config word from the LPA register though, there is still + * value in using the generic phy_resolve_aneg_linkmode function. So + * populate the supported and advertising link modes manually here. + */ + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, pcs->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); + if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX || + pcs->interface == PHY_INTERFACE_MODE_USXGMII) + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + pcs->supported); + if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX) + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + pcs->supported); + phy_advertise_supported(pcs); + + if (!phylink_autoneg_inband(link_an_mode)) + return; + switch (pcs->interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - vsc9959_pcs_an_restart_sgmii(pcs); + vsc9959_pcs_config_sgmii(pcs, link_an_mode, state); + break; + case PHY_INTERFACE_MODE_2500BASEX: + phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n"); break; case PHY_INTERFACE_MODE_USXGMII: - vsc9959_pcs_an_restart_usxgmii(pcs); + vsc9959_pcs_config_usxgmii(pcs, link_an_mode, state); break; default: - dev_err(ocelot->dev, "Invalid PCS interface type %s\n", + dev_err(ocelot->dev, "Unsupported link mode %s\n", phy_modes(pcs->interface)); - break; } } -/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the - * device tree. If we are in MLO_AN_PHY mode, we program directly state->speed - * into the PCS, which is retrieved out-of-band over MDIO. This also has the - * benefit of working with SGMII fixed-links, like downstream switches, where - * both link partners attempt to operate as AN slaves and therefore AN never - * completes. But it also has the disadvantage that some PHY chips don't pass - * traffic if SGMII AN is enabled but not completed (acknowledged by us), so - * setting MLO_AN_INBAND is actually required for those. - */ -static void vsc9959_pcs_init_sgmii(struct phy_device *pcs, - unsigned int link_an_mode, - const struct phylink_link_state *state) +static void vsc9959_pcs_link_up_sgmii(struct phy_device *pcs, + unsigned int link_an_mode, + int speed, int duplex) { - if (link_an_mode == MLO_AN_INBAND) { - int bmsr, bmcr; - - /* Some PHYs like VSC8234 don't like it when AN restarts on - * their system side and they restart line side AN too, going - * into an endless link up/down loop. Don't restart PCS AN if - * link is up already. - * We do check that AN is enabled just in case this is the 1st - * call, PCS detects a carrier but AN is disabled from power on - * or by boot loader. - */ - bmcr = phy_read(pcs, MII_BMCR); - if (bmcr < 0) - return; - - bmsr = phy_read(pcs, MII_BMSR); - if (bmsr < 0) - return; - - if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS)) - return; - - /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 - * for the MAC PCS in order to acknowledge the AN. - */ - phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | - ADVERTISE_LPACK); - - phy_write(pcs, ENETC_PCS_IF_MODE, - ENETC_PCS_IF_MODE_SGMII_EN | - ENETC_PCS_IF_MODE_USE_SGMII_AN); - - /* Adjust link timer for SGMII */ - phy_write(pcs, ENETC_PCS_LINK_TIMER1, - ENETC_PCS_LINK_TIMER1_VAL); - phy_write(pcs, ENETC_PCS_LINK_TIMER2, - ENETC_PCS_LINK_TIMER2_VAL); - - phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE); - } else { - int speed; - - if (state->duplex == DUPLEX_HALF) { - phydev_err(pcs, "Half duplex not supported\n"); - return; - } - switch (state->speed) { - case SPEED_1000: - speed = ENETC_PCS_SPEED_1000; - break; - case SPEED_100: - speed = ENETC_PCS_SPEED_100; - break; - case SPEED_10: - speed = ENETC_PCS_SPEED_10; - break; - case SPEED_UNKNOWN: - /* Silently don't do anything */ - return; - default: - phydev_err(pcs, "Invalid PCS speed %d\n", state->speed); - return; - } - - phy_write(pcs, ENETC_PCS_IF_MODE, - ENETC_PCS_IF_MODE_SGMII_EN | - ENETC_PCS_IF_MODE_SGMII_SPEED(speed)); + u16 if_mode = ENETC_PCS_IF_MODE_SGMII_EN; - /* Yes, not a mistake: speed is given by IF_MODE. */ - phy_write(pcs, MII_BMCR, BMCR_RESET | - BMCR_SPEED1000 | - BMCR_FULLDPLX); + switch (speed) { + case SPEED_1000: + if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_1000); + break; + case SPEED_100: + if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_100); + break; + case SPEED_10: + if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_10); + break; + default: + phydev_err(pcs, "Invalid PCS speed %d\n", speed); + return; } + + if (duplex == DUPLEX_HALF) + if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF; + + phy_write(pcs, ENETC_PCS_IF_MODE, if_mode); + phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE); } /* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane @@ -869,45 +930,24 @@ static void vsc9959_pcs_init_sgmii(struct phy_device *pcs, * lower link speed on line side, the system-side interface remains fixed at * 2500 Mbps and we do rate adaptation through pause frames. */ -static void vsc9959_pcs_init_2500basex(struct phy_device *pcs, - unsigned int link_an_mode, - const struct phylink_link_state *state) +static void vsc9959_pcs_link_up_2500basex(struct phy_device *pcs, + unsigned int link_an_mode, + int speed, int duplex) { - if (link_an_mode == MLO_AN_INBAND) { - phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n"); - return; - } - - phy_write(pcs, ENETC_PCS_IF_MODE, - ENETC_PCS_IF_MODE_SGMII_EN | - ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500)); - - phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | - BMCR_FULLDPLX | - BMCR_RESET); -} + u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) | + ENETC_PCS_IF_MODE_SGMII_EN; -static void vsc9959_pcs_init_usxgmii(struct phy_device *pcs, - unsigned int link_an_mode, - const struct phylink_link_state *state) -{ - if (link_an_mode != MLO_AN_INBAND) { - phydev_err(pcs, "USXGMII only supports in-band AN for now\n"); - return; - } + if (duplex == DUPLEX_HALF) + if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF; - /* Configure device ability for the USXGMII Replicator */ - phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, - USXGMII_ADVERTISE_SPEED(USXGMII_SPEED_2500) | - USXGMII_ADVERTISE_LNKS(1) | - ADVERTISE_SGMII | - ADVERTISE_LPACK | - USXGMII_ADVERTISE_FDX); + phy_write(pcs, ENETC_PCS_IF_MODE, if_mode); + phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE); } -static void vsc9959_pcs_init(struct ocelot *ocelot, int port, - unsigned int link_an_mode, - const struct phylink_link_state *state) +void vsc9959_pcs_link_up(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + phy_interface_t interface, + int speed, int duplex) { struct felix *felix = ocelot_to_felix(ocelot); struct phy_device *pcs = felix->pcs[port]; @@ -915,37 +955,20 @@ static void vsc9959_pcs_init(struct ocelot *ocelot, int port, if (!pcs) return; - /* The PCS does not implement the BMSR register fully, so capability - * detection via genphy_read_abilities does not work. Since we can get - * the PHY config word from the LPA register though, there is still - * value in using the generic phy_resolve_aneg_linkmode function. So - * populate the supported and advertising link modes manually here. - */ - linkmode_set_bit_array(phy_basic_ports_array, - ARRAY_SIZE(phy_basic_ports_array), - pcs->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); - if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX || - pcs->interface == PHY_INTERFACE_MODE_USXGMII) - linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, - pcs->supported); - if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX) - linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - pcs->supported); - phy_advertise_supported(pcs); + if (phylink_autoneg_inband(link_an_mode)) + return; - switch (pcs->interface) { + switch (interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - vsc9959_pcs_init_sgmii(pcs, link_an_mode, state); + vsc9959_pcs_link_up_sgmii(pcs, link_an_mode, speed, duplex); break; case PHY_INTERFACE_MODE_2500BASEX: - vsc9959_pcs_init_2500basex(pcs, link_an_mode, state); + vsc9959_pcs_link_up_2500basex(pcs, link_an_mode, speed, + duplex); break; case PHY_INTERFACE_MODE_USXGMII: - vsc9959_pcs_init_usxgmii(pcs, link_an_mode, state); + phydev_err(pcs, "USXGMII only supports in-band AN for now\n"); break; default: dev_err(ocelot->dev, "Unsupported link mode %s\n", @@ -1019,8 +1042,8 @@ static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs, return; pcs->autoneg = true; - pcs->autoneg_complete = USXGMII_BMSR_AN_CMPL(status); - pcs->link = USXGMII_BMSR_LNKS(status); + pcs->autoneg_complete = !!(status & BMSR_ANEGCOMPLETE); + pcs->link = !!(status & BMSR_LSTATUS); if (!pcs->link || !pcs->autoneg_complete) return; @@ -1029,31 +1052,31 @@ static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs, if (lpa < 0) return; - switch (USXGMII_LPA_SPEED(lpa)) { - case USXGMII_SPEED_10: + switch (lpa & MDIO_USXGMII_SPD_MASK) { + case MDIO_USXGMII_10: pcs->speed = SPEED_10; break; - case USXGMII_SPEED_100: + case MDIO_USXGMII_100: pcs->speed = SPEED_100; break; - case USXGMII_SPEED_1000: + case MDIO_USXGMII_1000: pcs->speed = SPEED_1000; break; - case USXGMII_SPEED_2500: + case MDIO_USXGMII_2500: pcs->speed = SPEED_2500; break; default: break; } - if (USXGMII_LPA_DUPLEX(lpa)) + if (lpa & MDIO_USXGMII_FULL_DUPLEX) pcs->duplex = DUPLEX_FULL; else pcs->duplex = DUPLEX_HALF; } -static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, - struct phylink_link_state *state) +void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, + struct phylink_link_state *state) { struct felix *felix = ocelot_to_felix(ocelot); struct phy_device *pcs = felix->pcs[port]; @@ -1084,6 +1107,43 @@ static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, vsc9959_pcs_link_state_resolve(pcs, state); } +static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 1000baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_INTERNAL || + state->interface == PHY_INTERFACE_MODE_2500BASEX || + state->interface == PHY_INTERFACE_MODE_USXGMII) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, phy_interface_t phy_mode) { @@ -1105,8 +1165,21 @@ static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, } } +/* Watermark encode + * Bit 8: Unit; 0:1, 1:16 + * Bit 7-0: Value to be multiplied with unit + */ +static u16 vsc9959_wm_enc(u16 value) +{ + if (value >= BIT(8)) + return BIT(8) | (value / 16); + + return value; +} + static const struct ocelot_ops vsc9959_ops = { .reset = vsc9959_reset, + .wm_enc = vsc9959_wm_enc, }; static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) @@ -1114,7 +1187,6 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) struct felix *felix = ocelot_to_felix(ocelot); struct enetc_mdio_priv *mdio_priv; struct device *dev = ocelot->dev; - resource_size_t imdio_base; void __iomem *imdio_regs; struct resource res; struct enetc_hw *hw; @@ -1130,13 +1202,10 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) return -ENOMEM; } - imdio_base = pci_resource_start(felix->pdev, - felix->info->imdio_pci_bar); - memcpy(&res, felix->info->imdio_res, sizeof(res)); res.flags = IORESOURCE_MEM; - res.start += imdio_base; - res.end += imdio_base; + res.start += felix->imdio_base; + res.end += felix->imdio_base; imdio_regs = devm_ioremap_resource(dev, &res); if (IS_ERR(imdio_regs)) { @@ -1196,7 +1265,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) return 0; } -static void vsc9959_mdio_bus_free(struct ocelot *ocelot) +void vsc9959_mdio_bus_free(struct ocelot *ocelot) { struct felix *felix = ocelot_to_felix(ocelot); int port; @@ -1392,7 +1461,25 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, } } -struct felix_info felix_info_vsc9959 = { +static void vsc9959_xmit_template_populate(struct ocelot *ocelot, int port) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 *template = ocelot_port->xmit_template; + u64 bypass, dest, src; + + /* Set the source port as the CPU port module and not the + * NPI port + */ + src = ocelot->num_phys_ports; + dest = BIT(port); + bypass = true; + + packing(template, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); + packing(template, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0); + packing(template, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0); +} + +static const struct felix_info felix_info_vsc9959 = { .target_io_res = vsc9959_target_io_res, .port_io_res = vsc9959_port_io_res, .imdio_res = &vsc9959_imdio_res, @@ -1412,10 +1499,151 @@ struct felix_info felix_info_vsc9959 = { .imdio_pci_bar = 0, .mdio_bus_alloc = vsc9959_mdio_bus_alloc, .mdio_bus_free = vsc9959_mdio_bus_free, - .pcs_init = vsc9959_pcs_init, - .pcs_an_restart = vsc9959_pcs_an_restart, + .pcs_config = vsc9959_pcs_config, + .pcs_link_up = vsc9959_pcs_link_up, .pcs_link_state = vsc9959_pcs_link_state, + .phylink_validate = vsc9959_phylink_validate, .prevalidate_phy_mode = vsc9959_prevalidate_phy_mode, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, + .xmit_template_populate = vsc9959_xmit_template_populate, +}; + +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; + 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; + } + + /* set up for high or low dma */ + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, + "DMA configuration failed: 0x%x\n", err); + goto err_dma; + } + } + + 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; + } + + pci_set_drvdata(pdev, felix); + ocelot = &felix->ocelot; + ocelot->dev = &pdev->dev; + felix->info = &felix_info_vsc9959; + felix->switch_base = pci_resource_start(pdev, + felix->info->switch_pci_bar); + felix->imdio_base = pci_resource_start(pdev, + felix->info->imdio_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; + + 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; + + err = dsa_register_switch(ds); + if (err) { + dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err); + goto err_register_ds; + } + + return 0; + +err_register_ds: + kfree(ds); +err_alloc_ds: +err_alloc_irq: +err_alloc_felix: + kfree(felix); +err_dma: + pci_disable_device(pdev); +err_pci_enable: + return err; +} + +static void felix_pci_remove(struct pci_dev *pdev) +{ + struct felix *felix; + + felix = pci_get_drvdata(pdev); + + dsa_unregister_switch(felix->ds); + + kfree(felix->ds); + kfree(felix); + + pci_disable_device(pdev); +} + +static struct pci_device_id felix_ids[] = { + { + /* NXP LS1028A */ + PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0xEEF0), + }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, felix_ids); + +struct pci_driver felix_vsc9959_pci_driver = { + .name = "mscc_felix", + .id_table = felix_ids, + .probe = felix_pci_probe, + .remove = felix_pci_remove, }; diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c new file mode 100644 index 000000000000..625b1891d955 --- /dev/null +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -0,0 +1,1104 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Distributed Switch Architecture VSC9953 driver + * Copyright (C) 2020, Maxim Kochetkov <fido_max@inbox.ru> + */ +#include <linux/types.h> +#include <soc/mscc/ocelot_vcap.h> +#include <soc/mscc/ocelot_sys.h> +#include <soc/mscc/ocelot.h> +#include <linux/of_platform.h> +#include <linux/packing.h> +#include <linux/iopoll.h> +#include "felix.h" + +#define VSC9953_VCAP_IS2_CNT 1024 +#define VSC9953_VCAP_IS2_ENTRY_WIDTH 376 +#define VSC9953_VCAP_PORT_CNT 10 + +#define MSCC_MIIM_REG_STATUS 0x0 +#define MSCC_MIIM_STATUS_STAT_BUSY BIT(3) +#define MSCC_MIIM_REG_CMD 0x8 +#define MSCC_MIIM_CMD_OPR_WRITE BIT(1) +#define MSCC_MIIM_CMD_OPR_READ BIT(2) +#define MSCC_MIIM_CMD_WRDATA_SHIFT 4 +#define MSCC_MIIM_CMD_REGAD_SHIFT 20 +#define MSCC_MIIM_CMD_PHYAD_SHIFT 25 +#define MSCC_MIIM_CMD_VLD BIT(31) +#define MSCC_MIIM_REG_DATA 0xC +#define MSCC_MIIM_DATA_ERROR (BIT(16) | BIT(17)) + +#define MSCC_PHY_REG_PHY_CFG 0x0 +#define PHY_CFG_PHY_ENA (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#define PHY_CFG_PHY_COMMON_RESET BIT(4) +#define PHY_CFG_PHY_RESET (BIT(5) | BIT(6) | BIT(7) | BIT(8)) +#define MSCC_PHY_REG_PHY_STATUS 0x4 + +static const u32 vsc9953_ana_regmap[] = { + REG(ANA_ADVLEARN, 0x00b500), + REG(ANA_VLANMASK, 0x00b504), + REG_RESERVED(ANA_PORT_B_DOMAIN), + REG(ANA_ANAGEFIL, 0x00b50c), + REG(ANA_ANEVENTS, 0x00b510), + REG(ANA_STORMLIMIT_BURST, 0x00b514), + REG(ANA_STORMLIMIT_CFG, 0x00b518), + REG(ANA_ISOLATED_PORTS, 0x00b528), + REG(ANA_COMMUNITY_PORTS, 0x00b52c), + REG(ANA_AUTOAGE, 0x00b530), + REG(ANA_MACTOPTIONS, 0x00b534), + REG(ANA_LEARNDISC, 0x00b538), + REG(ANA_AGENCTRL, 0x00b53c), + REG(ANA_MIRRORPORTS, 0x00b540), + REG(ANA_EMIRRORPORTS, 0x00b544), + REG(ANA_FLOODING, 0x00b548), + REG(ANA_FLOODING_IPMC, 0x00b54c), + REG(ANA_SFLOW_CFG, 0x00b550), + REG(ANA_PORT_MODE, 0x00b57c), + REG_RESERVED(ANA_CUT_THRU_CFG), + REG(ANA_PGID_PGID, 0x00b600), + REG(ANA_TABLES_ANMOVED, 0x00b4ac), + REG(ANA_TABLES_MACHDATA, 0x00b4b0), + REG(ANA_TABLES_MACLDATA, 0x00b4b4), + REG_RESERVED(ANA_TABLES_STREAMDATA), + REG(ANA_TABLES_MACACCESS, 0x00b4b8), + REG(ANA_TABLES_MACTINDX, 0x00b4bc), + REG(ANA_TABLES_VLANACCESS, 0x00b4c0), + REG(ANA_TABLES_VLANTIDX, 0x00b4c4), + REG_RESERVED(ANA_TABLES_ISDXACCESS), + REG_RESERVED(ANA_TABLES_ISDXTIDX), + REG(ANA_TABLES_ENTRYLIM, 0x00b480), + REG_RESERVED(ANA_TABLES_PTP_ID_HIGH), + REG_RESERVED(ANA_TABLES_PTP_ID_LOW), + REG_RESERVED(ANA_TABLES_STREAMACCESS), + REG_RESERVED(ANA_TABLES_STREAMTIDX), + REG_RESERVED(ANA_TABLES_SEQ_HISTORY), + REG_RESERVED(ANA_TABLES_SEQ_MASK), + REG_RESERVED(ANA_TABLES_SFID_MASK), + REG_RESERVED(ANA_TABLES_SFIDACCESS), + REG_RESERVED(ANA_TABLES_SFIDTIDX), + REG_RESERVED(ANA_MSTI_STATE), + REG_RESERVED(ANA_OAM_UPM_LM_CNT), + REG_RESERVED(ANA_SG_ACCESS_CTRL), + REG_RESERVED(ANA_SG_CONFIG_REG_1), + REG_RESERVED(ANA_SG_CONFIG_REG_2), + REG_RESERVED(ANA_SG_CONFIG_REG_3), + REG_RESERVED(ANA_SG_CONFIG_REG_4), + REG_RESERVED(ANA_SG_CONFIG_REG_5), + REG_RESERVED(ANA_SG_GCL_GS_CONFIG), + REG_RESERVED(ANA_SG_GCL_TI_CONFIG), + REG_RESERVED(ANA_SG_STATUS_REG_1), + REG_RESERVED(ANA_SG_STATUS_REG_2), + REG_RESERVED(ANA_SG_STATUS_REG_3), + REG(ANA_PORT_VLAN_CFG, 0x000000), + REG(ANA_PORT_DROP_CFG, 0x000004), + REG(ANA_PORT_QOS_CFG, 0x000008), + REG(ANA_PORT_VCAP_CFG, 0x00000c), + REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x000010), + REG(ANA_PORT_VCAP_S2_CFG, 0x00001c), + REG(ANA_PORT_PCP_DEI_MAP, 0x000020), + REG(ANA_PORT_CPU_FWD_CFG, 0x000060), + REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x000064), + REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x000068), + REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00006c), + REG(ANA_PORT_PORT_CFG, 0x000070), + REG(ANA_PORT_POL_CFG, 0x000074), + REG_RESERVED(ANA_PORT_PTP_CFG), + REG_RESERVED(ANA_PORT_PTP_DLY1_CFG), + REG_RESERVED(ANA_PORT_PTP_DLY2_CFG), + REG_RESERVED(ANA_PORT_SFID_CFG), + REG(ANA_PFC_PFC_CFG, 0x00c000), + REG_RESERVED(ANA_PFC_PFC_TIMER), + REG_RESERVED(ANA_IPT_OAM_MEP_CFG), + REG_RESERVED(ANA_IPT_IPT), + REG_RESERVED(ANA_PPT_PPT), + REG_RESERVED(ANA_FID_MAP_FID_MAP), + REG(ANA_AGGR_CFG, 0x00c600), + REG(ANA_CPUQ_CFG, 0x00c604), + REG_RESERVED(ANA_CPUQ_CFG2), + REG(ANA_CPUQ_8021_CFG, 0x00c60c), + REG(ANA_DSCP_CFG, 0x00c64c), + REG(ANA_DSCP_REWR_CFG, 0x00c74c), + REG(ANA_VCAP_RNG_TYPE_CFG, 0x00c78c), + REG(ANA_VCAP_RNG_VAL_CFG, 0x00c7ac), + REG_RESERVED(ANA_VRAP_CFG), + REG_RESERVED(ANA_VRAP_HDR_DATA), + REG_RESERVED(ANA_VRAP_HDR_MASK), + REG(ANA_DISCARD_CFG, 0x00c7d8), + REG(ANA_FID_CFG, 0x00c7dc), + REG(ANA_POL_PIR_CFG, 0x00a000), + REG(ANA_POL_CIR_CFG, 0x00a004), + REG(ANA_POL_MODE_CFG, 0x00a008), + REG(ANA_POL_PIR_STATE, 0x00a00c), + REG(ANA_POL_CIR_STATE, 0x00a010), + REG_RESERVED(ANA_POL_STATE), + REG(ANA_POL_FLOWC, 0x00c280), + REG(ANA_POL_HYST, 0x00c2ec), + REG_RESERVED(ANA_POL_MISC_CFG), +}; + +static const u32 vsc9953_qs_regmap[] = { + REG(QS_XTR_GRP_CFG, 0x000000), + REG(QS_XTR_RD, 0x000008), + REG(QS_XTR_FRM_PRUNING, 0x000010), + REG(QS_XTR_FLUSH, 0x000018), + REG(QS_XTR_DATA_PRESENT, 0x00001c), + REG(QS_XTR_CFG, 0x000020), + REG(QS_INJ_GRP_CFG, 0x000024), + REG(QS_INJ_WR, 0x00002c), + REG(QS_INJ_CTRL, 0x000034), + REG(QS_INJ_STATUS, 0x00003c), + REG(QS_INJ_ERR, 0x000040), + REG_RESERVED(QS_INH_DBG), +}; + +static const u32 vsc9953_s2_regmap[] = { + REG(S2_CORE_UPDATE_CTRL, 0x000000), + REG(S2_CORE_MV_CFG, 0x000004), + REG(S2_CACHE_ENTRY_DAT, 0x000008), + REG(S2_CACHE_MASK_DAT, 0x000108), + REG(S2_CACHE_ACTION_DAT, 0x000208), + REG(S2_CACHE_CNT_DAT, 0x000308), + REG(S2_CACHE_TG_DAT, 0x000388), +}; + +static const u32 vsc9953_qsys_regmap[] = { + REG(QSYS_PORT_MODE, 0x003600), + REG(QSYS_SWITCH_PORT_MODE, 0x003630), + REG(QSYS_STAT_CNT_CFG, 0x00365c), + REG(QSYS_EEE_CFG, 0x003660), + REG(QSYS_EEE_THRES, 0x003688), + REG(QSYS_IGR_NO_SHARING, 0x00368c), + REG(QSYS_EGR_NO_SHARING, 0x003690), + REG(QSYS_SW_STATUS, 0x003694), + REG(QSYS_EXT_CPU_CFG, 0x0036c0), + REG_RESERVED(QSYS_PAD_CFG), + REG(QSYS_CPU_GROUP_MAP, 0x0036c8), + REG_RESERVED(QSYS_QMAP), + REG_RESERVED(QSYS_ISDX_SGRP), + REG_RESERVED(QSYS_TIMED_FRAME_ENTRY), + REG_RESERVED(QSYS_TFRM_MISC), + REG_RESERVED(QSYS_TFRM_PORT_DLY), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_1), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_2), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_3), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_4), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_5), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_6), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_7), + REG_RESERVED(QSYS_TFRM_TIMER_CFG_8), + REG(QSYS_RED_PROFILE, 0x003724), + REG(QSYS_RES_QOS_MODE, 0x003764), + REG(QSYS_RES_CFG, 0x004000), + REG(QSYS_RES_STAT, 0x004004), + REG(QSYS_EGR_DROP_MODE, 0x003768), + REG(QSYS_EQ_CTRL, 0x00376c), + REG_RESERVED(QSYS_EVENTS_CORE), + REG_RESERVED(QSYS_QMAXSDU_CFG_0), + REG_RESERVED(QSYS_QMAXSDU_CFG_1), + REG_RESERVED(QSYS_QMAXSDU_CFG_2), + REG_RESERVED(QSYS_QMAXSDU_CFG_3), + REG_RESERVED(QSYS_QMAXSDU_CFG_4), + REG_RESERVED(QSYS_QMAXSDU_CFG_5), + REG_RESERVED(QSYS_QMAXSDU_CFG_6), + REG_RESERVED(QSYS_QMAXSDU_CFG_7), + REG_RESERVED(QSYS_PREEMPTION_CFG), + REG(QSYS_CIR_CFG, 0x000000), + REG_RESERVED(QSYS_EIR_CFG), + REG(QSYS_SE_CFG, 0x000008), + REG(QSYS_SE_DWRR_CFG, 0x00000c), + REG_RESERVED(QSYS_SE_CONNECT), + REG_RESERVED(QSYS_SE_DLB_SENSE), + REG(QSYS_CIR_STATE, 0x000044), + REG_RESERVED(QSYS_EIR_STATE), + REG_RESERVED(QSYS_SE_STATE), + REG(QSYS_HSCH_MISC_CFG, 0x003774), + REG_RESERVED(QSYS_TAG_CONFIG), + REG_RESERVED(QSYS_TAS_PARAM_CFG_CTRL), + REG_RESERVED(QSYS_PORT_MAX_SDU), + REG_RESERVED(QSYS_PARAM_CFG_REG_1), + REG_RESERVED(QSYS_PARAM_CFG_REG_2), + REG_RESERVED(QSYS_PARAM_CFG_REG_3), + REG_RESERVED(QSYS_PARAM_CFG_REG_4), + REG_RESERVED(QSYS_PARAM_CFG_REG_5), + REG_RESERVED(QSYS_GCL_CFG_REG_1), + REG_RESERVED(QSYS_GCL_CFG_REG_2), + REG_RESERVED(QSYS_PARAM_STATUS_REG_1), + REG_RESERVED(QSYS_PARAM_STATUS_REG_2), + REG_RESERVED(QSYS_PARAM_STATUS_REG_3), + REG_RESERVED(QSYS_PARAM_STATUS_REG_4), + REG_RESERVED(QSYS_PARAM_STATUS_REG_5), + REG_RESERVED(QSYS_PARAM_STATUS_REG_6), + REG_RESERVED(QSYS_PARAM_STATUS_REG_7), + REG_RESERVED(QSYS_PARAM_STATUS_REG_8), + REG_RESERVED(QSYS_PARAM_STATUS_REG_9), + REG_RESERVED(QSYS_GCL_STATUS_REG_1), + REG_RESERVED(QSYS_GCL_STATUS_REG_2), +}; + +static const u32 vsc9953_rew_regmap[] = { + REG(REW_PORT_VLAN_CFG, 0x000000), + REG(REW_TAG_CFG, 0x000004), + REG(REW_PORT_CFG, 0x000008), + REG(REW_DSCP_CFG, 0x00000c), + REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010), + REG_RESERVED(REW_PTP_CFG), + REG_RESERVED(REW_PTP_DLY1_CFG), + REG_RESERVED(REW_RED_TAG_CFG), + REG(REW_DSCP_REMAP_DP1_CFG, 0x000610), + REG(REW_DSCP_REMAP_CFG, 0x000710), + REG_RESERVED(REW_STAT_CFG), + REG_RESERVED(REW_REW_STICKY), + REG_RESERVED(REW_PPT), +}; + +static const u32 vsc9953_sys_regmap[] = { + REG(SYS_COUNT_RX_OCTETS, 0x000000), + REG(SYS_COUNT_RX_MULTICAST, 0x000008), + REG(SYS_COUNT_RX_SHORTS, 0x000010), + REG(SYS_COUNT_RX_FRAGMENTS, 0x000014), + REG(SYS_COUNT_RX_JABBERS, 0x000018), + REG(SYS_COUNT_RX_64, 0x000024), + REG(SYS_COUNT_RX_65_127, 0x000028), + REG(SYS_COUNT_RX_128_255, 0x00002c), + REG(SYS_COUNT_RX_256_1023, 0x000030), + REG(SYS_COUNT_RX_1024_1526, 0x000034), + REG(SYS_COUNT_RX_1527_MAX, 0x000038), + REG(SYS_COUNT_RX_LONGS, 0x000048), + REG(SYS_COUNT_TX_OCTETS, 0x000100), + REG(SYS_COUNT_TX_COLLISION, 0x000110), + REG(SYS_COUNT_TX_DROPS, 0x000114), + REG(SYS_COUNT_TX_64, 0x00011c), + REG(SYS_COUNT_TX_65_127, 0x000120), + REG(SYS_COUNT_TX_128_511, 0x000124), + REG(SYS_COUNT_TX_512_1023, 0x000128), + REG(SYS_COUNT_TX_1024_1526, 0x00012c), + REG(SYS_COUNT_TX_1527_MAX, 0x000130), + REG(SYS_COUNT_TX_AGING, 0x000178), + REG(SYS_RESET_CFG, 0x000318), + REG_RESERVED(SYS_SR_ETYPE_CFG), + REG(SYS_VLAN_ETYPE_CFG, 0x000320), + REG(SYS_PORT_MODE, 0x000324), + REG(SYS_FRONT_PORT_MODE, 0x000354), + REG(SYS_FRM_AGING, 0x00037c), + REG(SYS_STAT_CFG, 0x000380), + REG_RESERVED(SYS_SW_STATUS), + REG_RESERVED(SYS_MISC_CFG), + REG_RESERVED(SYS_REW_MAC_HIGH_CFG), + REG_RESERVED(SYS_REW_MAC_LOW_CFG), + REG_RESERVED(SYS_TIMESTAMP_OFFSET), + REG(SYS_PAUSE_CFG, 0x00044c), + REG(SYS_PAUSE_TOT_CFG, 0x000478), + REG(SYS_ATOP, 0x00047c), + REG(SYS_ATOP_TOT_CFG, 0x0004a8), + REG(SYS_MAC_FC_CFG, 0x0004ac), + REG(SYS_MMGT, 0x0004d4), + REG_RESERVED(SYS_MMGT_FAST), + REG_RESERVED(SYS_EVENTS_DIF), + REG_RESERVED(SYS_EVENTS_CORE), + REG_RESERVED(SYS_CNT), + REG_RESERVED(SYS_PTP_STATUS), + REG_RESERVED(SYS_PTP_TXSTAMP), + REG_RESERVED(SYS_PTP_NXT), + REG_RESERVED(SYS_PTP_CFG), + REG_RESERVED(SYS_RAM_INIT), + REG_RESERVED(SYS_CM_ADDR), + REG_RESERVED(SYS_CM_DATA_WR), + REG_RESERVED(SYS_CM_DATA_RD), + REG_RESERVED(SYS_CM_OP), + REG_RESERVED(SYS_CM_DATA), +}; + +static const u32 vsc9953_gcb_regmap[] = { + REG(GCB_SOFT_RST, 0x000008), + REG(GCB_MIIM_MII_STATUS, 0x0000ac), + REG(GCB_MIIM_MII_CMD, 0x0000b4), + REG(GCB_MIIM_MII_DATA, 0x0000b8), +}; + +static const u32 vsc9953_dev_gmii_regmap[] = { + REG(DEV_CLOCK_CFG, 0x0), + REG(DEV_PORT_MISC, 0x4), + REG_RESERVED(DEV_EVENTS), + REG(DEV_EEE_CFG, 0xc), + REG_RESERVED(DEV_RX_PATH_DELAY), + REG_RESERVED(DEV_TX_PATH_DELAY), + REG_RESERVED(DEV_PTP_PREDICT_CFG), + REG(DEV_MAC_ENA_CFG, 0x10), + REG(DEV_MAC_MODE_CFG, 0x14), + REG(DEV_MAC_MAXLEN_CFG, 0x18), + REG(DEV_MAC_TAGS_CFG, 0x1c), + REG(DEV_MAC_ADV_CHK_CFG, 0x20), + REG(DEV_MAC_IFG_CFG, 0x24), + REG(DEV_MAC_HDX_CFG, 0x28), + REG_RESERVED(DEV_MAC_DBG_CFG), + REG(DEV_MAC_FC_MAC_LOW_CFG, 0x30), + REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x34), + REG(DEV_MAC_STICKY, 0x38), + REG_RESERVED(PCS1G_CFG), + REG_RESERVED(PCS1G_MODE_CFG), + REG_RESERVED(PCS1G_SD_CFG), + REG_RESERVED(PCS1G_ANEG_CFG), + REG_RESERVED(PCS1G_ANEG_NP_CFG), + REG_RESERVED(PCS1G_LB_CFG), + REG_RESERVED(PCS1G_DBG_CFG), + REG_RESERVED(PCS1G_CDET_CFG), + REG_RESERVED(PCS1G_ANEG_STATUS), + REG_RESERVED(PCS1G_ANEG_NP_STATUS), + REG_RESERVED(PCS1G_LINK_STATUS), + REG_RESERVED(PCS1G_LINK_DOWN_CNT), + REG_RESERVED(PCS1G_STICKY), + REG_RESERVED(PCS1G_DEBUG_STATUS), + REG_RESERVED(PCS1G_LPI_CFG), + REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT), + REG_RESERVED(PCS1G_LPI_STATUS), + REG_RESERVED(PCS1G_TSTPAT_MODE_CFG), + REG_RESERVED(PCS1G_TSTPAT_STATUS), + REG_RESERVED(DEV_PCS_FX100_CFG), + REG_RESERVED(DEV_PCS_FX100_STATUS), +}; + +static const u32 *vsc9953_regmap[TARGET_MAX] = { + [ANA] = vsc9953_ana_regmap, + [QS] = vsc9953_qs_regmap, + [QSYS] = vsc9953_qsys_regmap, + [REW] = vsc9953_rew_regmap, + [SYS] = vsc9953_sys_regmap, + [S2] = vsc9953_s2_regmap, + [GCB] = vsc9953_gcb_regmap, + [DEV_GMII] = vsc9953_dev_gmii_regmap, +}; + +/* Addresses are relative to the device's base address */ +static const struct resource vsc9953_target_io_res[TARGET_MAX] = { + [ANA] = { + .start = 0x0280000, + .end = 0x028ffff, + .name = "ana", + }, + [QS] = { + .start = 0x0080000, + .end = 0x00800ff, + .name = "qs", + }, + [QSYS] = { + .start = 0x0200000, + .end = 0x021ffff, + .name = "qsys", + }, + [REW] = { + .start = 0x0030000, + .end = 0x003ffff, + .name = "rew", + }, + [SYS] = { + .start = 0x0010000, + .end = 0x001ffff, + .name = "sys", + }, + [S2] = { + .start = 0x0060000, + .end = 0x00603ff, + .name = "s2", + }, + [PTP] = { + .start = 0x0090000, + .end = 0x00900cb, + .name = "ptp", + }, + [GCB] = { + .start = 0x0070000, + .end = 0x00701ff, + .name = "devcpu_gcb", + }, +}; + +static const struct resource vsc9953_port_io_res[] = { + { + .start = 0x0100000, + .end = 0x010ffff, + .name = "port0", + }, + { + .start = 0x0110000, + .end = 0x011ffff, + .name = "port1", + }, + { + .start = 0x0120000, + .end = 0x012ffff, + .name = "port2", + }, + { + .start = 0x0130000, + .end = 0x013ffff, + .name = "port3", + }, + { + .start = 0x0140000, + .end = 0x014ffff, + .name = "port4", + }, + { + .start = 0x0150000, + .end = 0x015ffff, + .name = "port5", + }, + { + .start = 0x0160000, + .end = 0x016ffff, + .name = "port6", + }, + { + .start = 0x0170000, + .end = 0x017ffff, + .name = "port7", + }, + { + .start = 0x0180000, + .end = 0x018ffff, + .name = "port8", + }, + { + .start = 0x0190000, + .end = 0x019ffff, + .name = "port9", + }, +}; + +static const struct reg_field vsc9953_regfields[REGFIELD_MAX] = { + [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 10, 10), + [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 9), + [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24), + [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22), + [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21), + [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20), + [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19), + [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18), + [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17), + [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16), + [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15), + [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13), + [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12), + [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11), + [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10), + [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9), + [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8), + [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7), + [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6), + [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5), + [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4), + [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3), + [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2), + [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1), + [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0), + [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 16, 16), + [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 11, 12), + [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 10), + [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 7, 7), + [SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 6, 6), + [SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 5, 5), + [GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0), + [GCB_MIIM_MII_STATUS_PENDING] = REG_FIELD(GCB_MIIM_MII_STATUS, 2, 2), + [GCB_MIIM_MII_STATUS_BUSY] = REG_FIELD(GCB_MIIM_MII_STATUS, 3, 3), + /* Replicated per number of ports (11), register size 4 per port */ + [QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 13, 13, 11, 4), + [QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 11, 4), + [QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 11, 4), + [QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 11, 4), + [QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 11, 4), + [SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 4, 5, 11, 4), + [SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 2, 3, 11, 4), + [SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 11, 4), + [SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 11, 20, 11, 4), + [SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 10, 11, 4), + [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 11, 4), +}; + +static const struct ocelot_stat_layout vsc9953_stats_layout[] = { + { .offset = 0x00, .name = "rx_octets", }, + { .offset = 0x01, .name = "rx_unicast", }, + { .offset = 0x02, .name = "rx_multicast", }, + { .offset = 0x03, .name = "rx_broadcast", }, + { .offset = 0x04, .name = "rx_shorts", }, + { .offset = 0x05, .name = "rx_fragments", }, + { .offset = 0x06, .name = "rx_jabbers", }, + { .offset = 0x07, .name = "rx_crc_align_errs", }, + { .offset = 0x08, .name = "rx_sym_errs", }, + { .offset = 0x09, .name = "rx_frames_below_65_octets", }, + { .offset = 0x0A, .name = "rx_frames_65_to_127_octets", }, + { .offset = 0x0B, .name = "rx_frames_128_to_255_octets", }, + { .offset = 0x0C, .name = "rx_frames_256_to_511_octets", }, + { .offset = 0x0D, .name = "rx_frames_512_to_1023_octets", }, + { .offset = 0x0E, .name = "rx_frames_1024_to_1526_octets", }, + { .offset = 0x0F, .name = "rx_frames_over_1526_octets", }, + { .offset = 0x10, .name = "rx_pause", }, + { .offset = 0x11, .name = "rx_control", }, + { .offset = 0x12, .name = "rx_longs", }, + { .offset = 0x13, .name = "rx_classified_drops", }, + { .offset = 0x14, .name = "rx_red_prio_0", }, + { .offset = 0x15, .name = "rx_red_prio_1", }, + { .offset = 0x16, .name = "rx_red_prio_2", }, + { .offset = 0x17, .name = "rx_red_prio_3", }, + { .offset = 0x18, .name = "rx_red_prio_4", }, + { .offset = 0x19, .name = "rx_red_prio_5", }, + { .offset = 0x1A, .name = "rx_red_prio_6", }, + { .offset = 0x1B, .name = "rx_red_prio_7", }, + { .offset = 0x1C, .name = "rx_yellow_prio_0", }, + { .offset = 0x1D, .name = "rx_yellow_prio_1", }, + { .offset = 0x1E, .name = "rx_yellow_prio_2", }, + { .offset = 0x1F, .name = "rx_yellow_prio_3", }, + { .offset = 0x20, .name = "rx_yellow_prio_4", }, + { .offset = 0x21, .name = "rx_yellow_prio_5", }, + { .offset = 0x22, .name = "rx_yellow_prio_6", }, + { .offset = 0x23, .name = "rx_yellow_prio_7", }, + { .offset = 0x24, .name = "rx_green_prio_0", }, + { .offset = 0x25, .name = "rx_green_prio_1", }, + { .offset = 0x26, .name = "rx_green_prio_2", }, + { .offset = 0x27, .name = "rx_green_prio_3", }, + { .offset = 0x28, .name = "rx_green_prio_4", }, + { .offset = 0x29, .name = "rx_green_prio_5", }, + { .offset = 0x2A, .name = "rx_green_prio_6", }, + { .offset = 0x2B, .name = "rx_green_prio_7", }, + { .offset = 0x40, .name = "tx_octets", }, + { .offset = 0x41, .name = "tx_unicast", }, + { .offset = 0x42, .name = "tx_multicast", }, + { .offset = 0x43, .name = "tx_broadcast", }, + { .offset = 0x44, .name = "tx_collision", }, + { .offset = 0x45, .name = "tx_drops", }, + { .offset = 0x46, .name = "tx_pause", }, + { .offset = 0x47, .name = "tx_frames_below_65_octets", }, + { .offset = 0x48, .name = "tx_frames_65_to_127_octets", }, + { .offset = 0x49, .name = "tx_frames_128_255_octets", }, + { .offset = 0x4A, .name = "tx_frames_256_511_octets", }, + { .offset = 0x4B, .name = "tx_frames_512_1023_octets", }, + { .offset = 0x4C, .name = "tx_frames_1024_1526_octets", }, + { .offset = 0x4D, .name = "tx_frames_over_1526_octets", }, + { .offset = 0x4E, .name = "tx_yellow_prio_0", }, + { .offset = 0x4F, .name = "tx_yellow_prio_1", }, + { .offset = 0x50, .name = "tx_yellow_prio_2", }, + { .offset = 0x51, .name = "tx_yellow_prio_3", }, + { .offset = 0x52, .name = "tx_yellow_prio_4", }, + { .offset = 0x53, .name = "tx_yellow_prio_5", }, + { .offset = 0x54, .name = "tx_yellow_prio_6", }, + { .offset = 0x55, .name = "tx_yellow_prio_7", }, + { .offset = 0x56, .name = "tx_green_prio_0", }, + { .offset = 0x57, .name = "tx_green_prio_1", }, + { .offset = 0x58, .name = "tx_green_prio_2", }, + { .offset = 0x59, .name = "tx_green_prio_3", }, + { .offset = 0x5A, .name = "tx_green_prio_4", }, + { .offset = 0x5B, .name = "tx_green_prio_5", }, + { .offset = 0x5C, .name = "tx_green_prio_6", }, + { .offset = 0x5D, .name = "tx_green_prio_7", }, + { .offset = 0x5E, .name = "tx_aged", }, + { .offset = 0x80, .name = "drop_local", }, + { .offset = 0x81, .name = "drop_tail", }, + { .offset = 0x82, .name = "drop_yellow_prio_0", }, + { .offset = 0x83, .name = "drop_yellow_prio_1", }, + { .offset = 0x84, .name = "drop_yellow_prio_2", }, + { .offset = 0x85, .name = "drop_yellow_prio_3", }, + { .offset = 0x86, .name = "drop_yellow_prio_4", }, + { .offset = 0x87, .name = "drop_yellow_prio_5", }, + { .offset = 0x88, .name = "drop_yellow_prio_6", }, + { .offset = 0x89, .name = "drop_yellow_prio_7", }, + { .offset = 0x8A, .name = "drop_green_prio_0", }, + { .offset = 0x8B, .name = "drop_green_prio_1", }, + { .offset = 0x8C, .name = "drop_green_prio_2", }, + { .offset = 0x8D, .name = "drop_green_prio_3", }, + { .offset = 0x8E, .name = "drop_green_prio_4", }, + { .offset = 0x8F, .name = "drop_green_prio_5", }, + { .offset = 0x90, .name = "drop_green_prio_6", }, + { .offset = 0x91, .name = "drop_green_prio_7", }, +}; + +static struct vcap_field vsc9953_vcap_is2_keys[] = { + /* Common: 41 bits */ + [VCAP_IS2_TYPE] = { 0, 4}, + [VCAP_IS2_HK_FIRST] = { 4, 1}, + [VCAP_IS2_HK_PAG] = { 5, 8}, + [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 11}, + [VCAP_IS2_HK_RSV2] = { 24, 1}, + [VCAP_IS2_HK_HOST_MATCH] = { 25, 1}, + [VCAP_IS2_HK_L2_MC] = { 26, 1}, + [VCAP_IS2_HK_L2_BC] = { 27, 1}, + [VCAP_IS2_HK_VLAN_TAGGED] = { 28, 1}, + [VCAP_IS2_HK_VID] = { 29, 12}, + [VCAP_IS2_HK_DEI] = { 41, 1}, + [VCAP_IS2_HK_PCP] = { 42, 3}, + /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ + [VCAP_IS2_HK_L2_DMAC] = { 45, 48}, + [VCAP_IS2_HK_L2_SMAC] = { 93, 48}, + /* MAC_ETYPE (TYPE=000) */ + [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {141, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {157, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {173, 8}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {181, 3}, + /* MAC_LLC (TYPE=001) */ + [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {141, 40}, + /* MAC_SNAP (TYPE=010) */ + [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {141, 40}, + /* MAC_ARP (TYPE=011) */ + [VCAP_IS2_HK_MAC_ARP_SMAC] = { 45, 48}, + [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 93, 1}, + [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 94, 1}, + [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 95, 1}, + [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 96, 1}, + [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 97, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 98, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE] = { 99, 2}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {101, 32}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {133, 32}, + [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {165, 1}, + /* IP4_TCP_UDP / IP4_OTHER common */ + [VCAP_IS2_HK_IP4] = { 45, 1}, + [VCAP_IS2_HK_L3_FRAGMENT] = { 46, 1}, + [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 47, 1}, + [VCAP_IS2_HK_L3_OPTIONS] = { 48, 1}, + [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 49, 1}, + [VCAP_IS2_HK_L3_TOS] = { 50, 8}, + [VCAP_IS2_HK_L3_IP4_DIP] = { 58, 32}, + [VCAP_IS2_HK_L3_IP4_SIP] = { 90, 32}, + [VCAP_IS2_HK_DIP_EQ_SIP] = {122, 1}, + /* IP4_TCP_UDP (TYPE=100) */ + [VCAP_IS2_HK_TCP] = {123, 1}, + [VCAP_IS2_HK_L4_SPORT] = {124, 16}, + [VCAP_IS2_HK_L4_DPORT] = {140, 16}, + [VCAP_IS2_HK_L4_RNG] = {156, 8}, + [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {164, 1}, + [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {165, 1}, + [VCAP_IS2_HK_L4_URG] = {166, 1}, + [VCAP_IS2_HK_L4_ACK] = {167, 1}, + [VCAP_IS2_HK_L4_PSH] = {168, 1}, + [VCAP_IS2_HK_L4_RST] = {169, 1}, + [VCAP_IS2_HK_L4_SYN] = {170, 1}, + [VCAP_IS2_HK_L4_FIN] = {171, 1}, + /* IP4_OTHER (TYPE=101) */ + [VCAP_IS2_HK_IP4_L3_PROTO] = {123, 8}, + [VCAP_IS2_HK_L3_PAYLOAD] = {131, 56}, + /* IP6_STD (TYPE=110) */ + [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 45, 1}, + [VCAP_IS2_HK_L3_IP6_SIP] = { 46, 128}, + [VCAP_IS2_HK_IP6_L3_PROTO] = {174, 8}, +}; + +static struct vcap_field vsc9953_vcap_is2_actions[] = { + [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, + [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, + [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, + [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, + [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, + [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, + [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, + [VCAP_IS2_ACT_POLICE_IDX] = { 10, 8}, + [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 21, 1}, + [VCAP_IS2_ACT_PORT_MASK] = { 22, 10}, + [VCAP_IS2_ACT_ACL_ID] = { 44, 6}, + [VCAP_IS2_ACT_HIT_CNT] = { 50, 32}, +}; + +static const struct vcap_props vsc9953_vcap_props[] = { + [VCAP_IS2] = { + .tg_width = 2, + .sw_count = 4, + .entry_count = VSC9953_VCAP_IS2_CNT, + .entry_width = VSC9953_VCAP_IS2_ENTRY_WIDTH, + .action_count = VSC9953_VCAP_IS2_CNT + + VSC9953_VCAP_PORT_CNT + 2, + .action_width = 101, + .action_type_width = 1, + .action_table = { + [IS2_ACTION_TYPE_NORMAL] = { + .width = 44, + .count = 2 + }, + [IS2_ACTION_TYPE_SMAC_SIP] = { + .width = 6, + .count = 4 + }, + }, + .counter_words = 4, + .counter_width = 32, + }, +}; + +#define VSC9953_INIT_TIMEOUT 50000 +#define VSC9953_GCB_RST_SLEEP 100 +#define VSC9953_SYS_RAMINIT_SLEEP 80 +#define VCS9953_MII_TIMEOUT 10000 + +static int vsc9953_gcb_soft_rst_status(struct ocelot *ocelot) +{ + int val; + + ocelot_field_read(ocelot, GCB_SOFT_RST_SWC_RST, &val); + + return val; +} + +static int vsc9953_sys_ram_init_status(struct ocelot *ocelot) +{ + int val; + + ocelot_field_read(ocelot, SYS_RESET_CFG_MEM_INIT, &val); + + return val; +} + +static int vsc9953_gcb_miim_pending_status(struct ocelot *ocelot) +{ + int val; + + ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_PENDING, &val); + + return val; +} + +static int vsc9953_gcb_miim_busy_status(struct ocelot *ocelot) +{ + int val; + + ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_BUSY, &val); + + return val; +} + +static int vsc9953_mdio_write(struct mii_bus *bus, int phy_id, int regnum, + u16 value) +{ + struct ocelot *ocelot = bus->priv; + int err, cmd, val; + + /* Wait while MIIM controller becomes idle */ + err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot, + val, !val, 10, VCS9953_MII_TIMEOUT); + if (err) { + dev_err(ocelot->dev, "MDIO write: pending timeout\n"); + goto out; + } + + cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | + (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | + (value << MSCC_MIIM_CMD_WRDATA_SHIFT) | + MSCC_MIIM_CMD_OPR_WRITE; + + ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD); + +out: + return err; +} + +static int vsc9953_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + struct ocelot *ocelot = bus->priv; + int err, cmd, val; + + /* Wait until MIIM controller becomes idle */ + err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot, + val, !val, 10, VCS9953_MII_TIMEOUT); + if (err) { + dev_err(ocelot->dev, "MDIO read: pending timeout\n"); + goto out; + } + + /* Write the MIIM COMMAND register */ + cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | + (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ; + + ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD); + + /* Wait while read operation via the MIIM controller is in progress */ + err = readx_poll_timeout(vsc9953_gcb_miim_busy_status, ocelot, + val, !val, 10, VCS9953_MII_TIMEOUT); + if (err) { + dev_err(ocelot->dev, "MDIO read: busy timeout\n"); + goto out; + } + + val = ocelot_read(ocelot, GCB_MIIM_MII_DATA); + + err = val & 0xFFFF; +out: + return err; +} + +static int vsc9953_reset(struct ocelot *ocelot) +{ + int val, err; + + /* soft-reset the switch core */ + ocelot_field_write(ocelot, GCB_SOFT_RST_SWC_RST, 1); + + err = readx_poll_timeout(vsc9953_gcb_soft_rst_status, ocelot, val, !val, + VSC9953_GCB_RST_SLEEP, VSC9953_INIT_TIMEOUT); + if (err) { + dev_err(ocelot->dev, "timeout: switch core reset\n"); + return err; + } + + /* initialize switch mem ~40us */ + ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_INIT, 1); + ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_ENA, 1); + + err = readx_poll_timeout(vsc9953_sys_ram_init_status, ocelot, val, !val, + VSC9953_SYS_RAMINIT_SLEEP, + VSC9953_INIT_TIMEOUT); + if (err) { + dev_err(ocelot->dev, "timeout: switch sram init\n"); + return err; + } + + /* enable switch core */ + ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_ENA, 1); + ocelot_field_write(ocelot, SYS_RESET_CFG_CORE_ENA, 1); + + return 0; +} + +static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 1000baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_INTERNAL) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static int vsc9953_prevalidate_phy_mode(struct ocelot *ocelot, int port, + phy_interface_t phy_mode) +{ + switch (phy_mode) { + case PHY_INTERFACE_MODE_INTERNAL: + if (port != 8 && port != 9) + return -ENOTSUPP; + return 0; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + /* Not supported on internal to-CPU ports */ + if (port == 8 || port == 9) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } +} + +/* Watermark encode + * Bit 9: Unit; 0:1, 1:16 + * Bit 8-0: Value to be multiplied with unit + */ +static u16 vsc9953_wm_enc(u16 value) +{ + if (value >= BIT(9)) + return BIT(9) | (value / 16); + + return value; +} + +static const struct ocelot_ops vsc9953_ops = { + .reset = vsc9953_reset, + .wm_enc = vsc9953_wm_enc, +}; + +static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct device *dev = ocelot->dev; + struct mii_bus *bus; + int port; + int rc; + + felix->pcs = devm_kcalloc(dev, felix->info->num_ports, + sizeof(struct phy_device *), + GFP_KERNEL); + if (!felix->pcs) { + dev_err(dev, "failed to allocate array for PCS PHYs\n"); + return -ENOMEM; + } + + bus = devm_mdiobus_alloc(dev); + if (!bus) + return -ENOMEM; + + bus->name = "VSC9953 internal MDIO bus"; + bus->read = vsc9953_mdio_read; + bus->write = vsc9953_mdio_write; + bus->parent = dev; + bus->priv = ocelot; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); + + /* Needed in order to initialize the bus mutex lock */ + rc = mdiobus_register(bus); + if (rc < 0) { + dev_err(dev, "failed to register MDIO bus\n"); + return rc; + } + + felix->imdio = bus; + + for (port = 0; port < felix->info->num_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct phy_device *pcs; + int addr = port + 4; + + if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL) + continue; + + pcs = get_phy_device(felix->imdio, addr, false); + if (IS_ERR(pcs)) + continue; + + pcs->interface = ocelot_port->phy_mode; + felix->pcs[port] = pcs; + + dev_info(dev, "Found PCS at internal MDIO address %d\n", addr); + } + + return 0; +} + +static void vsc9953_xmit_template_populate(struct ocelot *ocelot, int port) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 *template = ocelot_port->xmit_template; + u64 bypass, dest, src; + + /* Set the source port as the CPU port module and not the + * NPI port + */ + src = ocelot->num_phys_ports; + dest = BIT(port); + bypass = true; + + packing(template, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); + packing(template, &dest, 67, 57, OCELOT_TAG_LEN, PACK, 0); + packing(template, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0); +} + +static const struct felix_info seville_info_vsc9953 = { + .target_io_res = vsc9953_target_io_res, + .port_io_res = vsc9953_port_io_res, + .regfields = vsc9953_regfields, + .map = vsc9953_regmap, + .ops = &vsc9953_ops, + .stats_layout = vsc9953_stats_layout, + .num_stats = ARRAY_SIZE(vsc9953_stats_layout), + .vcap_is2_keys = vsc9953_vcap_is2_keys, + .vcap_is2_actions = vsc9953_vcap_is2_actions, + .vcap = vsc9953_vcap_props, + .shared_queue_sz = 128 * 1024, + .num_mact_rows = 2048, + .num_ports = 10, + .mdio_bus_alloc = vsc9953_mdio_bus_alloc, + .mdio_bus_free = vsc9959_mdio_bus_free, + .pcs_config = vsc9959_pcs_config, + .pcs_link_up = vsc9959_pcs_link_up, + .pcs_link_state = vsc9959_pcs_link_state, + .phylink_validate = vsc9953_phylink_validate, + .prevalidate_phy_mode = vsc9953_prevalidate_phy_mode, + .xmit_template_populate = vsc9953_xmit_template_populate, +}; + +static int seville_probe(struct platform_device *pdev) +{ + struct dsa_switch *ds; + struct ocelot *ocelot; + struct resource *res; + struct felix *felix; + int err; + + 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; + } + + platform_set_drvdata(pdev, felix); + + ocelot = &felix->ocelot; + ocelot->dev = &pdev->dev; + felix->info = &seville_info_vsc9953; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + felix->switch_base = res->start; + + 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->ops = &felix_switch_ops; + ds->priv = ocelot; + felix->ds = ds; + + err = dsa_register_switch(ds); + if (err) { + dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err); + goto err_register_ds; + } + + return 0; + +err_register_ds: + kfree(ds); +err_alloc_ds: +err_alloc_felix: + kfree(felix); + return err; +} + +static int seville_remove(struct platform_device *pdev) +{ + struct felix *felix; + + felix = platform_get_drvdata(pdev); + + dsa_unregister_switch(felix->ds); + + kfree(felix->ds); + kfree(felix); + + return 0; +} + +static const struct of_device_id seville_of_match[] = { + { .compatible = "mscc,vsc9953-switch" }, + { }, +}; +MODULE_DEVICE_TABLE(of, seville_of_match); + +struct platform_driver seville_vsc9953_driver = { + .probe = seville_probe, + .remove = seville_remove, + .driver = { + .name = "mscc_seville", + .of_match_table = of_match_ptr(seville_of_match), + }, +}; diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 7c86056b9401..e24a99031b80 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -97,8 +97,7 @@ (AR9331_SW_PORT_STATUS_TXMAC | AR9331_SW_PORT_STATUS_RXMAC) #define AR9331_SW_PORT_STATUS_LINK_MASK \ - (AR9331_SW_PORT_STATUS_LINK_EN | AR9331_SW_PORT_STATUS_FLOW_LINK_EN | \ - AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ + (AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ AR9331_SW_PORT_STATUS_SPEED_M) @@ -410,33 +409,10 @@ static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; int ret; - u32 val; - - switch (state->speed) { - case SPEED_1000: - val = AR9331_SW_PORT_STATUS_SPEED_1000; - break; - case SPEED_100: - val = AR9331_SW_PORT_STATUS_SPEED_100; - break; - case SPEED_10: - val = AR9331_SW_PORT_STATUS_SPEED_10; - break; - default: - return; - } - - if (state->duplex) - val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; - - if (state->pause & MLO_PAUSE_TX) - val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; - - if (state->pause & MLO_PAUSE_RX) - val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), - AR9331_SW_PORT_STATUS_LINK_MASK, val); + AR9331_SW_PORT_STATUS_LINK_EN | + AR9331_SW_PORT_STATUS_FLOW_LINK_EN, 0); if (ret) dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); } @@ -464,11 +440,37 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; + u32 val; int ret; + val = AR9331_SW_PORT_STATUS_MAC_MASK; + switch (speed) { + case SPEED_1000: + val |= AR9331_SW_PORT_STATUS_SPEED_1000; + break; + case SPEED_100: + val |= AR9331_SW_PORT_STATUS_SPEED_100; + break; + case SPEED_10: + val |= AR9331_SW_PORT_STATUS_SPEED_10; + break; + default: + return; + } + + if (duplex) + val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; + + if (tx_pause) + val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; + + if (rx_pause) + val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), - AR9331_SW_PORT_STATUS_MAC_MASK, - AR9331_SW_PORT_STATUS_MAC_MASK); + AR9331_SW_PORT_STATUS_MAC_MASK | + AR9331_SW_PORT_STATUS_LINK_MASK, + val); if (ret) dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); } diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index d2b5ab403e06..f1e484477e35 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -14,6 +14,7 @@ #include <linux/of_platform.h> #include <linux/if_bridge.h> #include <linux/mdio.h> +#include <linux/phylink.h> #include <linux/gpio/consumer.h> #include <linux/etherdevice.h> @@ -407,64 +408,121 @@ qca8k_fdb_flush(struct qca8k_priv *priv) mutex_unlock(&priv->reg_mutex); } -static void -qca8k_mib_init(struct qca8k_priv *priv) +static int +qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) { + u32 reg; + + /* Set the command and VLAN index */ + reg = QCA8K_VTU_FUNC1_BUSY; + reg |= cmd; + reg |= vid << QCA8K_VTU_FUNC1_VID_S; + + /* Write the function register triggering the table access */ + qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); + + /* wait for completion */ + if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY)) + return -ETIMEDOUT; + + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_VLAN_LOAD) { + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1); + if (reg & QCA8K_VTU_FUNC1_FULL) + return -ENOMEM; + } + + return 0; +} + +static int +qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +{ + u32 reg; + int ret; + + /* + We do the right thing with VLAN 0 and treat it as untagged while + preserving the tag on egress. + */ + if (vid == 0) + return 0; + mutex_lock(&priv->reg_mutex); - qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); - qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); - qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); - qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); + ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); + if (ret < 0) + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); + reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; + reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + if (untagged) + reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG << + QCA8K_VTU_FUNC0_EG_MODE_S(port); + else + reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG << + QCA8K_VTU_FUNC0_EG_MODE_S(port); + + qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + +out: mutex_unlock(&priv->reg_mutex); + + return ret; } static int -qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode) +qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) { - u32 reg, val; + u32 reg, mask; + int ret, i; + bool del; - switch (port) { - case 0: - reg = QCA8K_REG_PORT0_PAD_CTRL; - break; - case 6: - reg = QCA8K_REG_PORT6_PAD_CTRL; - break; - default: - pr_err("Can't set PAD_CTRL on port %d\n", port); - return -EINVAL; + mutex_lock(&priv->reg_mutex); + ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); + if (ret < 0) + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); + reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT << + QCA8K_VTU_FUNC0_EG_MODE_S(port); + + /* Check if we're the last member to be removed */ + del = true; + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + mask = QCA8K_VTU_FUNC0_EG_MODE_NOT; + mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i); + + if ((reg & mask) != mask) { + del = false; + break; + } } - /* Configure a port to be directly connected to an external - * PHY or MAC. - */ - switch (mode) { - case PHY_INTERFACE_MODE_RGMII: - /* RGMII mode means no delay so don't enable the delay */ - val = QCA8K_PORT_PAD_RGMII_EN; - qca8k_write(priv, reg, val); - break; - case PHY_INTERFACE_MODE_RGMII_ID: - /* RGMII_ID needs internal delay. This is enabled through - * PORT5_PAD_CTRL for all ports, rather than individual port - * registers - */ - qca8k_write(priv, reg, - QCA8K_PORT_PAD_RGMII_EN | - QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | - QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); - qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, - QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); - break; - case PHY_INTERFACE_MODE_SGMII: - qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - break; - default: - pr_err("xMII mode %d not supported\n", mode); - return -EINVAL; + if (del) { + ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); + } else { + qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); } - return 0; +out: + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static void +qca8k_mib_init(struct qca8k_priv *priv) +{ + mutex_lock(&priv->reg_mutex); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); + qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); + mutex_unlock(&priv->reg_mutex); } static void @@ -639,9 +697,7 @@ static int qca8k_setup(struct dsa_switch *ds) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - phy_interface_t phy_mode = PHY_INTERFACE_MODE_NA; int ret, i; - u32 mask; /* Make sure that port 0 is the cpu port */ if (!dsa_is_cpu_port(ds, 0)) { @@ -661,24 +717,9 @@ qca8k_setup(struct dsa_switch *ds) if (ret) return ret; - /* Initialize CPU port pad mode (xMII type, delays...) */ - ret = of_get_phy_mode(dsa_to_port(ds, QCA8K_CPU_PORT)->dn, &phy_mode); - if (ret) { - pr_err("Can't find phy-mode for master device\n"); - return ret; - } - ret = qca8k_set_pad_ctrl(priv, QCA8K_CPU_PORT, phy_mode); - if (ret < 0) - return ret; - - /* Enable CPU Port, force it to maximum bandwidth and full-duplex */ - mask = QCA8K_PORT_STATUS_SPEED_1000 | QCA8K_PORT_STATUS_TXFLOW | - QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_DUPLEX; - qca8k_write(priv, QCA8K_REG_PORT_STATUS(QCA8K_CPU_PORT), mask); + /* Enable CPU Port */ qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); - qca8k_port_set_status(priv, QCA8K_CPU_PORT, 1); - priv->port_sts[QCA8K_CPU_PORT].enabled = 1; /* Enable MIB counters */ qca8k_mib_init(priv); @@ -693,10 +734,9 @@ qca8k_setup(struct dsa_switch *ds) qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), QCA8K_PORT_LOOKUP_MEMBER, 0); - /* Disable MAC by default on all user ports */ + /* Disable MAC by default on all ports */ for (i = 1; i < QCA8K_NUM_PORTS; i++) - if (dsa_is_user_port(ds, i)) - qca8k_port_set_status(priv, i, 0); + qca8k_port_set_status(priv, i, 0); /* Forward all unknown frames to CPU port for Linux processing */ qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, @@ -713,7 +753,7 @@ qca8k_setup(struct dsa_switch *ds) QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); } - /* Invividual user ports get connected to CPU port only */ + /* Individual user ports get connected to CPU port only */ if (dsa_is_user_port(ds, i)) { int shift = 16 * (i % 2); @@ -729,58 +769,273 @@ qca8k_setup(struct dsa_switch *ds) * default egress vid */ qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), - 0xffff << shift, 1 << shift); + 0xfff << shift, + QCA8K_PORT_VID_DEF << shift); qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), - QCA8K_PORT_VLAN_CVID(1) | - QCA8K_PORT_VLAN_SVID(1)); + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); } } + /* Setup our port MTUs to match power on defaults */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) + priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; + qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); + /* Flush the FDB table */ qca8k_fdb_flush(priv); + /* We don't have interrupts for link changes, so we need to poll */ + ds->pcs_poll = true; + return 0; } static void -qca8k_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phy) +qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + const struct phylink_link_state *state) { struct qca8k_priv *priv = ds->priv; - u32 reg; + u32 reg, val; + + switch (port) { + case 0: /* 1st CPU port */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII) + return; + + reg = QCA8K_REG_PORT0_PAD_CTRL; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + /* Internal PHY, nothing to do */ + return; + case 6: /* 2nd CPU port / external PHY */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + return; + + reg = QCA8K_REG_PORT6_PAD_CTRL; + break; + default: + dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); + return; + } + + if (port != 6 && phylink_autoneg_inband(mode)) { + dev_err(ds->dev, "%s: in-band negotiation unsupported\n", + __func__); + return; + } + + switch (state->interface) { + case PHY_INTERFACE_MODE_RGMII: + /* RGMII mode means no delay so don't enable the delay */ + qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); + break; + case PHY_INTERFACE_MODE_RGMII_ID: + /* RGMII_ID needs internal delay. This is enabled through + * PORT5_PAD_CTRL for all ports, rather than individual port + * registers + */ + qca8k_write(priv, reg, + QCA8K_PORT_PAD_RGMII_EN | + QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | + QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); + qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + /* Enable SGMII on the port */ + qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - /* Force fixed-link setting for CPU port, skip others. */ - if (!phy_is_pseudo_fixed_link(phy)) + /* Enable/disable SerDes auto-negotiation as necessary */ + val = qca8k_read(priv, QCA8K_REG_PWS); + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else + val |= QCA8K_PWS_SERDES_AEN_DIS; + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ + val = qca8k_read(priv, QCA8K_REG_SGMII_CTRL); + + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD; + + if (dsa_is_cpu_port(ds, port)) { + /* CPU port, we're talking to the CPU MAC, be a PHY */ + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_PHY; + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_MAC; + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_BASEX; + } + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + break; + default: + dev_err(ds->dev, "xMII mode %s not supported for port %d\n", + phy_modes(state->interface), port); return; + } +} + +static void +qca8k_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - /* Set port speed */ - switch (phy->speed) { - case 10: - reg = QCA8K_PORT_STATUS_SPEED_10; + switch (port) { + case 0: /* 1st CPU port */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII) + goto unsupported; break; - case 100: - reg = QCA8K_PORT_STATUS_SPEED_100; + case 1: + case 2: + case 3: + case 4: + case 5: + /* Internal PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_GMII) + goto unsupported; break; - case 1000: - reg = QCA8K_PORT_STATUS_SPEED_1000; + case 6: /* 2nd CPU port / external PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + goto unsupported; break; default: - dev_dbg(priv->dev, "port%d link speed %dMbps not supported.\n", - port, phy->speed); +unsupported: + linkmode_zero(supported); return; } - /* Set duplex mode */ - if (phy->duplex == DUPLEX_FULL) - reg |= QCA8K_PORT_STATUS_DUPLEX; + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); - /* Force flow control */ - if (dsa_is_cpu_port(ds, port)) - reg |= QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_TXFLOW; + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phylink_set(mask, 1000baseX_Full); + + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); +} + +static int +qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct qca8k_priv *priv = ds->priv; + u32 reg; + + reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port)); + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; + state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); + state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : + DUPLEX_HALF; + + switch (reg & QCA8K_PORT_STATUS_SPEED) { + case QCA8K_PORT_STATUS_SPEED_10: + state->speed = SPEED_10; + break; + case QCA8K_PORT_STATUS_SPEED_100: + state->speed = SPEED_100; + break; + case QCA8K_PORT_STATUS_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + state->pause = MLO_PAUSE_NONE; + if (reg & QCA8K_PORT_STATUS_RXFLOW) + state->pause |= MLO_PAUSE_RX; + if (reg & QCA8K_PORT_STATUS_TXFLOW) + state->pause |= MLO_PAUSE_TX; + + return 1; +} + +static void +qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) +{ + struct qca8k_priv *priv = ds->priv; - /* Force link down before changing MAC options */ qca8k_port_set_status(priv, port, 0); +} + +static void +qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface, struct phy_device *phydev, + int speed, int duplex, bool tx_pause, bool rx_pause) +{ + struct qca8k_priv *priv = ds->priv; + u32 reg; + + if (phylink_autoneg_inband(mode)) { + reg = QCA8K_PORT_STATUS_LINK_AUTO; + } else { + switch (speed) { + case SPEED_10: + reg = QCA8K_PORT_STATUS_SPEED_10; + break; + case SPEED_100: + reg = QCA8K_PORT_STATUS_SPEED_100; + break; + case SPEED_1000: + reg = QCA8K_PORT_STATUS_SPEED_1000; + break; + default: + reg = QCA8K_PORT_STATUS_LINK_AUTO; + break; + } + + if (duplex == DUPLEX_FULL) + reg |= QCA8K_PORT_STATUS_DUPLEX; + + if (rx_pause || dsa_is_cpu_port(ds, port)) + reg |= QCA8K_PORT_STATUS_RXFLOW; + + if (tx_pause || dsa_is_cpu_port(ds, port)) + reg |= QCA8K_PORT_STATUS_TXFLOW; + } + + reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); - qca8k_port_set_status(priv, port, 1); } static void @@ -937,13 +1192,11 @@ qca8k_port_enable(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - if (!dsa_is_user_port(ds, port)) - return 0; - qca8k_port_set_status(priv, port, 1); priv->port_sts[port].enabled = 1; - phy_support_asym_pause(phy); + if (dsa_is_user_port(ds, port)) + phy_support_asym_pause(phy); return 0; } @@ -958,12 +1211,36 @@ qca8k_port_disable(struct dsa_switch *ds, int port) } static int +qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct qca8k_priv *priv = ds->priv; + int i, mtu = 0; + + priv->port_mtu[port] = new_mtu; + + for (i = 0; i < QCA8K_NUM_PORTS; i++) + if (priv->port_mtu[port] > mtu) + mtu = priv->port_mtu[port]; + + /* Include L2 header / FCS length */ + qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); + + return 0; +} + +static int +qca8k_port_max_mtu(struct dsa_switch *ds, int port) +{ + return QCA8K_MAX_MTU; +} + +static int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, u16 port_mask, u16 vid) { /* Set the vid to the port vlan id if no vid is set */ if (!vid) - vid = 1; + vid = QCA8K_PORT_VID_DEF; return qca8k_fdb_add(priv, addr, port_mask, vid, QCA8K_ATU_STATUS_STATIC); @@ -987,7 +1264,7 @@ qca8k_port_fdb_del(struct dsa_switch *ds, int port, u16 port_mask = BIT(port); if (!vid) - vid = 1; + vid = QCA8K_PORT_VID_DEF; return qca8k_fdb_del(priv, addr, port_mask, vid); } @@ -1016,6 +1293,76 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, return 0; } +static int +qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) +{ + struct qca8k_priv *priv = ds->priv; + + if (vlan_filtering) { + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE, + QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); + } else { + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE, + QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); + } + + return 0; +} + +static int +qca8k_port_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + return 0; +} + +static void +qca8k_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + struct qca8k_priv *priv = ds->priv; + int ret = 0; + u16 vid; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid) + ret = qca8k_vlan_add(priv, port, vid, untagged); + + if (ret) + dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); + + if (pvid) { + int shift = 16 * (port % 2); + + qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), + 0xfff << shift, + vlan->vid_end << shift); + qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), + QCA8K_PORT_VLAN_CVID(vlan->vid_end) | + QCA8K_PORT_VLAN_SVID(vlan->vid_end)); + } +} + +static int +qca8k_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct qca8k_priv *priv = ds->priv; + int ret = 0; + u16 vid; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid) + ret = qca8k_vlan_del(priv, port, vid); + + if (ret) + dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); + + return ret; +} + static enum dsa_tag_protocol qca8k_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -1026,7 +1373,6 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port, static const struct dsa_switch_ops qca8k_switch_ops = { .get_tag_protocol = qca8k_get_tag_protocol, .setup = qca8k_setup, - .adjust_link = qca8k_adjust_link, .get_strings = qca8k_get_strings, .get_ethtool_stats = qca8k_get_ethtool_stats, .get_sset_count = qca8k_get_sset_count, @@ -1034,12 +1380,23 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .set_mac_eee = qca8k_set_mac_eee, .port_enable = qca8k_port_enable, .port_disable = qca8k_port_disable, + .port_change_mtu = qca8k_port_change_mtu, + .port_max_mtu = qca8k_port_max_mtu, .port_stp_state_set = qca8k_port_stp_state_set, .port_bridge_join = qca8k_port_bridge_join, .port_bridge_leave = qca8k_port_bridge_leave, .port_fdb_add = qca8k_port_fdb_add, .port_fdb_del = qca8k_port_fdb_del, .port_fdb_dump = qca8k_port_fdb_dump, + .port_vlan_filtering = qca8k_port_vlan_filtering, + .port_vlan_prepare = qca8k_port_vlan_prepare, + .port_vlan_add = qca8k_port_vlan_add, + .port_vlan_del = qca8k_port_vlan_del, + .phylink_validate = qca8k_phylink_validate, + .phylink_mac_link_state = qca8k_phylink_mac_link_state, + .phylink_mac_config = qca8k_phylink_mac_config, + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, }; static int @@ -1085,6 +1442,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->ds->dev = &mdiodev->dev; priv->ds->num_ports = QCA8K_NUM_PORTS; + priv->ds->configure_vlan_while_not_filtering = true; priv->ds->priv = priv; priv->ops = qca8k_switch_ops; priv->ds->ops = &priv->ops; diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 42d6ea24eb14..7ca4b93e0bb5 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -13,6 +13,7 @@ #include <linux/gpio.h> #define QCA8K_NUM_PORTS 7 +#define QCA8K_MAX_MTU 9000 #define PHY_ID_QCA8337 0x004dd036 #define QCA8K_ID_QCA8337 0x13 @@ -21,6 +22,8 @@ #define QCA8K_CPU_PORT 0 +#define QCA8K_PORT_VID_DEF 1 + /* Global control registers */ #define QCA8K_REG_MASK_CTRL 0x000 #define QCA8K_MASK_CTRL_ID_M 0xff @@ -36,6 +39,8 @@ #define QCA8K_MAX_DELAY 3 #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) #define QCA8K_PORT_PAD_SGMII_EN BIT(7) +#define QCA8K_REG_PWS 0x010 +#define QCA8K_PWS_SERDES_AEN_DIS BIT(7) #define QCA8K_REG_MODULE_EN 0x030 #define QCA8K_MODULE_EN_MIB BIT(0) #define QCA8K_REG_MIB 0x034 @@ -56,6 +61,7 @@ #define QCA8K_MDIO_MASTER_MAX_REG 32 #define QCA8K_GOL_MAC_ADDR0 0x60 #define QCA8K_GOL_MAC_ADDR1 0x64 +#define QCA8K_MAX_FRAME_SIZE 0x78 #define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) #define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0) #define QCA8K_PORT_STATUS_SPEED_10 0 @@ -69,6 +75,7 @@ #define QCA8K_PORT_STATUS_LINK_UP BIT(8) #define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) #define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) +#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12) #define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) #define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) #define QCA8K_PORT_HDR_CTRL_RX_S 2 @@ -77,6 +84,16 @@ #define QCA8K_PORT_HDR_CTRL_ALL 2 #define QCA8K_PORT_HDR_CTRL_MGMT 1 #define QCA8K_PORT_HDR_CTRL_NONE 0 +#define QCA8K_REG_SGMII_CTRL 0x0e0 +#define QCA8K_SGMII_EN_PLL BIT(1) +#define QCA8K_SGMII_EN_RX BIT(2) +#define QCA8K_SGMII_EN_TX BIT(3) +#define QCA8K_SGMII_EN_SD BIT(4) +#define QCA8K_SGMII_CLK125M_DELAY BIT(7) +#define QCA8K_SGMII_MODE_CTRL_MASK (BIT(22) | BIT(23)) +#define QCA8K_SGMII_MODE_CTRL_BASEX (0 << 22) +#define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22) +#define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22) /* EEE control registers */ #define QCA8K_REG_EEE_CTRL 0x100 @@ -111,6 +128,19 @@ #define QCA8K_ATU_FUNC_FULL BIT(12) #define QCA8K_ATU_FUNC_PORT_M 0xf #define QCA8K_ATU_FUNC_PORT_S 8 +#define QCA8K_REG_VTU_FUNC0 0x610 +#define QCA8K_VTU_FUNC0_VALID BIT(20) +#define QCA8K_VTU_FUNC0_IVL_EN BIT(19) +#define QCA8K_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2) +#define QCA8K_VTU_FUNC0_EG_MODE_MASK 3 +#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD 0 +#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG 1 +#define QCA8K_VTU_FUNC0_EG_MODE_TAG 2 +#define QCA8K_VTU_FUNC0_EG_MODE_NOT 3 +#define QCA8K_REG_VTU_FUNC1 0x614 +#define QCA8K_VTU_FUNC1_BUSY BIT(31) +#define QCA8K_VTU_FUNC1_VID_S 16 +#define QCA8K_VTU_FUNC1_FULL BIT(4) #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 @@ -120,6 +150,11 @@ #define QCA8K_GLOBAL_FW_CTRL1_UC_DP_S 0 #define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) #define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0) +#define QCA8K_PORT_LOOKUP_VLAN_MODE GENMASK(9, 8) +#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE (0 << 8) +#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK (1 << 8) +#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK (2 << 8) +#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE (3 << 8) #define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16) #define QCA8K_PORT_LOOKUP_STATE_DISABLED (0 << 16) #define QCA8K_PORT_LOOKUP_STATE_BLOCKING (1 << 16) @@ -163,6 +198,15 @@ enum qca8k_fdb_cmd { QCA8K_FDB_SEARCH = 7, }; +enum qca8k_vlan_cmd { + QCA8K_VLAN_FLUSH = 1, + QCA8K_VLAN_LOAD = 2, + QCA8K_VLAN_PURGE = 3, + QCA8K_VLAN_REMOVE_PORT = 4, + QCA8K_VLAN_NEXT = 5, + QCA8K_VLAN_READ = 6, +}; + struct ar8xxx_port_status { int enabled; }; @@ -176,6 +220,7 @@ struct qca8k_priv { struct device *dev; struct dsa_switch_ops ops; struct gpio_desc *reset_gpio; + unsigned int port_mtu[QCA8K_NUM_PORTS]; }; struct qca8k_mib_desc { diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/rtl8366.c index ac88caca5ad4..8f40fbf70a82 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/rtl8366.c @@ -43,18 +43,26 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, int ret; int i; + dev_dbg(smi->dev, + "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", + vid, member, untag); + /* Update the 4K table */ ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); if (ret) return ret; - vlan4k.member = member; - vlan4k.untag = untag; + vlan4k.member |= member; + vlan4k.untag |= untag; vlan4k.fid = fid; ret = smi->ops->set_vlan_4k(smi, &vlan4k); if (ret) return ret; + dev_dbg(smi->dev, + "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", + vid, vlan4k.member, vlan4k.untag); + /* Try to find an existing MC entry for this VID */ for (i = 0; i < smi->num_vlan_mc; i++) { struct rtl8366_vlan_mc vlanmc; @@ -65,11 +73,16 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, if (vid == vlanmc.vid) { /* update the MC entry */ - vlanmc.member = member; - vlanmc.untag = untag; + vlanmc.member |= member; + vlanmc.untag |= untag; vlanmc.fid = fid; ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + + dev_dbg(smi->dev, + "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", + vid, vlanmc.member, vlanmc.untag); + break; } } @@ -272,7 +285,7 @@ int rtl8366_init_vlan(struct realtek_smi *smi) /* For the CPU port, make all ports members of this * VLAN. */ - mask = GENMASK(smi->num_ports - 1, 0); + mask = GENMASK((int)smi->num_ports - 1, 0); else /* For all other ports, enable itself plus the * CPU port. @@ -384,7 +397,7 @@ void rtl8366_vlan_add(struct dsa_switch *ds, int port, if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) dev_err(smi->dev, "port is DSA or CPU port\n"); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { int pvid_val = 0; dev_info(smi->dev, "add VLAN %04x\n", vid); @@ -407,13 +420,13 @@ void rtl8366_vlan_add(struct dsa_switch *ds, int port, if (ret < 0) return; } - } - ret = rtl8366_set_vlan(smi, port, member, untag, 0); - if (ret) - dev_err(smi->dev, - "failed to set up VLAN %04x", - vid); + ret = rtl8366_set_vlan(smi, vid, member, untag, 0); + if (ret) + dev_err(smi->dev, + "failed to set up VLAN %04x", + vid); + } } EXPORT_SYMBOL_GPL(rtl8366_vlan_add); diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index fd1977590cb4..48f1ff746799 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -109,8 +109,8 @@ /* CPU port control reg */ #define RTL8368RB_CPU_CTRL_REG 0x0061 #define RTL8368RB_CPU_PORTS_MSK 0x00FF -/* Enables inserting custom tag length/type 0x8899 */ -#define RTL8368RB_CPU_INSTAG BIT(15) +/* Disables inserting custom tag length/type 0x8899 */ +#define RTL8368RB_CPU_NO_TAG BIT(15) #define RTL8366RB_SMAR0 0x0070 /* bits 0..15 */ #define RTL8366RB_SMAR1 0x0071 /* bits 16..31 */ @@ -844,16 +844,14 @@ static int rtl8366rb_setup(struct dsa_switch *ds) if (ret) return ret; - /* Enable CPU port and enable inserting CPU tag + /* Enable CPU port with custom DSA tag 8899. * - * Disabling RTL8368RB_CPU_INSTAG here will change the behaviour - * of the switch totally and it will start talking Realtek RRCP - * internally. It is probably possible to experiment with this, - * but then the kernel needs to understand and handle RRCP first. + * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers + * the custom tag is turned off. */ ret = regmap_update_bits(smi->map, RTL8368RB_CPU_CTRL_REG, 0xFFFF, - RTL8368RB_CPU_INSTAG | BIT(smi->cpu_port)); + BIT(smi->cpu_port)); if (ret) return ret; @@ -967,21 +965,8 @@ static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) { - /* For now, the RTL switches are handled without any custom tags. - * - * It is possible to turn on "custom tags" by removing the - * RTL8368RB_CPU_INSTAG flag when enabling the port but what it - * does is unfamiliar to DSA: ethernet frames of type 8899, the Realtek - * Remote Control Protocol (RRCP) start to appear on the CPU port of - * the device. So this is not the ordinary few extra bytes in the - * frame. Instead it appears that the switch starts to talk Realtek - * RRCP internally which means a pretty complex RRCP implementation - * decoding and responding the RRCP protocol is needed to exploit this. - * - * The OpenRRCP project (dormant since 2009) have reverse-egineered - * parts of the protocol. - */ - return DSA_TAG_PROTO_NONE; + /* This switch uses the 4 byte protocol A Realtek DSA tag */ + return DSA_TAG_PROTO_RTL4_A; } static void rtl8366rb_adjust_link(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 29ed21687295..ba70b40a9a95 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -262,12 +262,12 @@ int sja1105_static_config_upload(struct sja1105_private *priv); int sja1105_inhibit_tx(const struct sja1105_private *priv, unsigned long port_bitmap, bool tx_inhibited); -extern struct sja1105_info sja1105e_info; -extern struct sja1105_info sja1105t_info; -extern struct sja1105_info sja1105p_info; -extern struct sja1105_info sja1105q_info; -extern struct sja1105_info sja1105r_info; -extern struct sja1105_info sja1105s_info; +extern const struct sja1105_info sja1105e_info; +extern const struct sja1105_info sja1105t_info; +extern const struct sja1105_info sja1105p_info; +extern const struct sja1105_info sja1105q_info; +extern const struct sja1105_info sja1105r_info; +extern const struct sja1105_info sja1105s_info; /* From sja1105_clocking.c */ diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 4471eeccc293..75247f342124 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -638,9 +638,7 @@ static size_t sja1105pqrs_cbs_entry_packing(void *buf, void *entry_ptr, #define OP_SEARCH BIT(3) /* SJA1105E/T: First generation */ -struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, +const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_VL_LOOKUP] = { .entry_packing = sja1105et_vl_lookup_entry_packing, .cmd_packing = sja1105_vl_lookup_cmd_packing, @@ -649,8 +647,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, .addr = 0x35, }, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, [BLK_IDX_L2_LOOKUP] = { .entry_packing = sja1105et_dyn_l2_lookup_entry_packing, .cmd_packing = sja1105et_l2_lookup_cmd_packing, @@ -667,7 +663,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, .addr = 0x20, }, - [BLK_IDX_L2_POLICING] = {0}, [BLK_IDX_VLAN_LOOKUP] = { .entry_packing = sja1105_vlan_lookup_entry_packing, .cmd_packing = sja1105_vlan_lookup_cmd_packing, @@ -692,9 +687,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD, .addr = 0x36, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .entry_packing = sja1105et_l2_lookup_params_entry_packing, .cmd_packing = sja1105et_l2_lookup_params_cmd_packing, @@ -703,8 +695,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, .addr = 0x38, }, - [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, - [BLK_IDX_AVB_PARAMS] = {0}, [BLK_IDX_GENERAL_PARAMS] = { .entry_packing = sja1105et_general_params_entry_packing, .cmd_packing = sja1105et_general_params_cmd_packing, @@ -729,13 +719,10 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_CBS_DYN_CMD, .addr = 0x2c, }, - [BLK_IDX_XMII_PARAMS] = {0}, }; /* SJA1105P/Q/R/S: Second generation */ -struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, +const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_VL_LOOKUP] = { .entry_packing = sja1105_vl_lookup_entry_packing, .cmd_packing = sja1105_vl_lookup_cmd_packing, @@ -744,8 +731,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, .addr = 0x47, }, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, [BLK_IDX_L2_LOOKUP] = { .entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing, .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, @@ -762,7 +747,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, .addr = 0x24, }, - [BLK_IDX_L2_POLICING] = {0}, [BLK_IDX_VLAN_LOOKUP] = { .entry_packing = sja1105_vlan_lookup_entry_packing, .cmd_packing = sja1105_vlan_lookup_cmd_packing, @@ -787,9 +771,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD, .addr = 0x4B, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .entry_packing = sja1105pqrs_l2_lookup_params_entry_packing, .cmd_packing = sja1105pqrs_l2_lookup_params_cmd_packing, @@ -798,7 +779,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, .addr = 0x54, }, - [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, [BLK_IDX_AVB_PARAMS] = { .entry_packing = sja1105pqrs_avb_params_entry_packing, .cmd_packing = sja1105pqrs_avb_params_cmd_packing, @@ -831,7 +811,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_CBS_DYN_CMD, .addr = 0x32, }, - [BLK_IDX_XMII_PARAMS] = {0}, }; /* Provides read access to the settings through the dynamic interface diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h index 1fc0d13dc623..28d4eb5efb8b 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h @@ -34,7 +34,7 @@ struct sja1105_mgmt_entry { u64 index; }; -extern struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN]; -extern struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN]; +extern const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN]; +extern const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN]; #endif diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index 9ee8968610cd..12e76020bea3 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -31,7 +31,7 @@ static int sja1105_setup_bcast_policer(struct sja1105_private *priv, struct netlink_ext_ack *extack, unsigned long cookie, int port, u64 rate_bytes_per_sec, - s64 burst) + u32 burst) { struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); struct sja1105_l2_policing_entry *policing; @@ -79,9 +79,8 @@ static int sja1105_setup_bcast_policer(struct sja1105_private *priv, policing[rule->bcast_pol.sharindx].rate = div_u64(rate_bytes_per_sec * 512, 1000000); - policing[rule->bcast_pol.sharindx].smax = div_u64(rate_bytes_per_sec * - PSCHED_NS2TICKS(burst), - PSCHED_TICKS_PER_SEC); + policing[rule->bcast_pol.sharindx].smax = burst; + /* TODO: support per-flow MTU */ policing[rule->bcast_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; @@ -103,7 +102,7 @@ static int sja1105_setup_tc_policer(struct sja1105_private *priv, struct netlink_ext_ack *extack, unsigned long cookie, int port, int tc, u64 rate_bytes_per_sec, - s64 burst) + u32 burst) { struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); struct sja1105_l2_policing_entry *policing; @@ -152,9 +151,8 @@ static int sja1105_setup_tc_policer(struct sja1105_private *priv, policing[rule->tc_pol.sharindx].rate = div_u64(rate_bytes_per_sec * 512, 1000000); - policing[rule->tc_pol.sharindx].smax = div_u64(rate_bytes_per_sec * - PSCHED_NS2TICKS(burst), - PSCHED_TICKS_PER_SEC); + policing[rule->tc_pol.sharindx].smax = burst; + /* TODO: support per-flow MTU */ policing[rule->tc_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; @@ -177,7 +175,7 @@ static int sja1105_flower_policer(struct sja1105_private *priv, int port, unsigned long cookie, struct sja1105_key *key, u64 rate_bytes_per_sec, - s64 burst) + u32 burst) { switch (key->type) { case SJA1105_KEY_BCAST: diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 789b288cc78b..c3f6f124e5f0 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -3324,9 +3324,7 @@ static int sja1105_port_policer_add(struct dsa_switch *ds, int port, */ policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, 1000000); - policing[port].smax = div_u64(policer->rate_bytes_per_sec * - PSCHED_NS2TICKS(policer->burst), - PSCHED_TICKS_PER_SEC); + policing[port].smax = policer->burst; return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); } @@ -3393,11 +3391,14 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .devlink_param_set = sja1105_devlink_param_set, }; +static const struct of_device_id sja1105_dt_ids[]; + static int sja1105_check_device_id(struct sja1105_private *priv) { const struct sja1105_regs *regs = priv->info->regs; u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0}; struct device *dev = &priv->spidev->dev; + const struct of_device_id *match; u32 device_id; u64 part_no; int rc; @@ -3407,12 +3408,6 @@ static int sja1105_check_device_id(struct sja1105_private *priv) if (rc < 0) return rc; - if (device_id != priv->info->device_id) { - dev_err(dev, "Expected device ID 0x%llx but read 0x%x\n", - priv->info->device_id, device_id); - return -ENODEV; - } - rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id, SJA1105_SIZE_DEVICE_ID); if (rc < 0) @@ -3420,13 +3415,29 @@ static int sja1105_check_device_id(struct sja1105_private *priv) sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID); - if (part_no != priv->info->part_no) { - dev_err(dev, "Expected part number 0x%llx but read 0x%llx\n", - priv->info->part_no, part_no); - return -ENODEV; + for (match = sja1105_dt_ids; match->compatible; match++) { + const struct sja1105_info *info = match->data; + + /* Is what's been probed in our match table at all? */ + if (info->device_id != device_id || info->part_no != part_no) + continue; + + /* But is it what's in the device tree? */ + if (priv->info->device_id != device_id || + priv->info->part_no != part_no) { + dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n", + priv->info->name, info->name); + /* It isn't. No problem, pick that up. */ + priv->info = info; + } + + return 0; } - return 0; + dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n", + device_id, part_no); + + return -ENODEV; } static int sja1105_probe(struct spi_device *spi) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 177134596458..1b90570b257b 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -24,7 +24,7 @@ * this hardware can do (but may be enough for some setups). Anything of higher * frequency than 1 Hz will be lost, since there is no timestamp FIFO. */ -#define SJA1105_EXTTS_INTERVAL (HZ / 4) +#define SJA1105_EXTTS_INTERVAL (HZ / 6) /* This range is actually +/- SJA1105_MAX_ADJ_PPB * divided by 1000 (ppb -> ppm) and with a 16-bit @@ -51,8 +51,8 @@ enum sja1105_ptp_clk_mode { PTP_SET_MODE = 0, }; -#define extts_to_data(d) \ - container_of((d), struct sja1105_ptp_data, extts_work) +#define extts_to_data(t) \ + container_of((t), struct sja1105_ptp_data, extts_timer) #define ptp_caps_to_data(d) \ container_of((d), struct sja1105_ptp_data, caps) #define ptp_data_to_sja1105(d) \ @@ -350,6 +350,30 @@ static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks, ptp_sts); } +static void sja1105_extts_poll(struct sja1105_private *priv) +{ + struct sja1105_ptp_data *ptp_data = &priv->ptp_data; + const struct sja1105_regs *regs = priv->info->regs; + struct ptp_clock_event event; + u64 ptpsyncts = 0; + int rc; + + rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts, + NULL); + if (rc < 0) + dev_err_ratelimited(priv->ds->dev, + "Failed to read PTPSYNCTS: %d\n", rc); + + if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) { + event.index = 0; + event.type = PTP_CLOCK_EXTTS; + event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts)); + ptp_clock_event(ptp_data->clock, &event); + + ptp_data->ptpsyncts = ptpsyncts; + } +} + static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) { struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); @@ -380,6 +404,9 @@ static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) netif_rx_ni(skb); } + if (ptp_data->extts_enabled) + sja1105_extts_poll(priv); + mutex_unlock(&ptp_data->lock); /* Don't restart */ @@ -595,36 +622,21 @@ static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) return rc; } -static void sja1105_ptp_extts_work(struct work_struct *work) +static void sja1105_ptp_extts_setup_timer(struct sja1105_ptp_data *ptp_data) { - struct delayed_work *dw = to_delayed_work(work); - struct sja1105_ptp_data *ptp_data = extts_to_data(dw); - struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); - const struct sja1105_regs *regs = priv->info->regs; - struct ptp_clock_event event; - u64 ptpsyncts = 0; - int rc; - - mutex_lock(&ptp_data->lock); - - rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts, - NULL); - if (rc < 0) - dev_err_ratelimited(priv->ds->dev, - "Failed to read PTPSYNCTS: %d\n", rc); + unsigned long expires = ((jiffies / SJA1105_EXTTS_INTERVAL) + 1) * + SJA1105_EXTTS_INTERVAL; - if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) { - event.index = 0; - event.type = PTP_CLOCK_EXTTS; - event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts)); - ptp_clock_event(ptp_data->clock, &event); + mod_timer(&ptp_data->extts_timer, expires); +} - ptp_data->ptpsyncts = ptpsyncts; - } +static void sja1105_ptp_extts_timer(struct timer_list *t) +{ + struct sja1105_ptp_data *ptp_data = extts_to_data(t); - mutex_unlock(&ptp_data->lock); + ptp_schedule_worker(ptp_data->clock, 0); - schedule_delayed_work(&ptp_data->extts_work, SJA1105_EXTTS_INTERVAL); + sja1105_ptp_extts_setup_timer(ptp_data); } static int sja1105_change_ptp_clk_pin_func(struct sja1105_private *priv, @@ -771,11 +783,12 @@ static int sja1105_extts_enable(struct sja1105_private *priv, if (rc) return rc; + priv->ptp_data.extts_enabled = on; + if (on) - schedule_delayed_work(&priv->ptp_data.extts_work, - SJA1105_EXTTS_INTERVAL); + sja1105_ptp_extts_setup_timer(&priv->ptp_data); else - cancel_delayed_work_sync(&priv->ptp_data.extts_work); + del_timer_sync(&priv->ptp_data.extts_timer); return 0; } @@ -858,7 +871,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds) ptp_data->cmd.corrclk4ts = true; ptp_data->cmd.ptpclkadd = PTP_SET_MODE; - INIT_DELAYED_WORK(&ptp_data->extts_work, sja1105_ptp_extts_work); + timer_setup(&ptp_data->extts_timer, sja1105_ptp_extts_timer, 0); return sja1105_ptp_reset(ds); } @@ -871,7 +884,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds) if (IS_ERR_OR_NULL(ptp_data->clock)) return; - cancel_delayed_work_sync(&ptp_data->extts_work); + del_timer_sync(&ptp_data->extts_timer); ptp_cancel_worker_sync(ptp_data->clock); skb_queue_purge(&ptp_data->skb_rxtstamp_queue); ptp_clock_unregister(ptp_data->clock); diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 6408d1158f2d..3daa33e98e77 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -4,6 +4,8 @@ #ifndef _SJA1105_PTP_H #define _SJA1105_PTP_H +#include <linux/timer.h> + #if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) /* Timestamps are in units of 8 ns clock ticks (equivalent to @@ -72,13 +74,14 @@ struct sja1105_ptp_cmd { }; struct sja1105_ptp_data { - struct delayed_work extts_work; + struct timer_list extts_timer; struct sk_buff_head skb_rxtstamp_queue; struct ptp_clock_info caps; struct ptp_clock *clock; struct sja1105_ptp_cmd cmd; /* Serializes all operations on the PTP hardware clock */ struct mutex lock; + bool extts_enabled; u64 ptpsyncts; }; diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index bb52b9c841b2..704dcf1d1c01 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -507,7 +507,7 @@ static struct sja1105_regs sja1105pqrs_regs = { .ptpsyncts = 0x1F, }; -struct sja1105_info sja1105e_info = { +const struct sja1105_info sja1105e_info = { .device_id = SJA1105E_DEVICE_ID, .part_no = SJA1105ET_PART_NO, .static_ops = sja1105e_table_ops, @@ -523,7 +523,8 @@ struct sja1105_info sja1105e_info = { .regs = &sja1105et_regs, .name = "SJA1105E", }; -struct sja1105_info sja1105t_info = { + +const struct sja1105_info sja1105t_info = { .device_id = SJA1105T_DEVICE_ID, .part_no = SJA1105ET_PART_NO, .static_ops = sja1105t_table_ops, @@ -539,7 +540,8 @@ struct sja1105_info sja1105t_info = { .regs = &sja1105et_regs, .name = "SJA1105T", }; -struct sja1105_info sja1105p_info = { + +const struct sja1105_info sja1105p_info = { .device_id = SJA1105PR_DEVICE_ID, .part_no = SJA1105P_PART_NO, .static_ops = sja1105p_table_ops, @@ -556,7 +558,8 @@ struct sja1105_info sja1105p_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105P", }; -struct sja1105_info sja1105q_info = { + +const struct sja1105_info sja1105q_info = { .device_id = SJA1105QS_DEVICE_ID, .part_no = SJA1105Q_PART_NO, .static_ops = sja1105q_table_ops, @@ -573,7 +576,8 @@ struct sja1105_info sja1105q_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105Q", }; -struct sja1105_info sja1105r_info = { + +const struct sja1105_info sja1105r_info = { .device_id = SJA1105PR_DEVICE_ID, .part_no = SJA1105R_PART_NO, .static_ops = sja1105r_table_ops, @@ -590,7 +594,8 @@ struct sja1105_info sja1105r_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105R", }; -struct sja1105_info sja1105s_info = { + +const struct sja1105_info sja1105s_info = { .device_id = SJA1105QS_DEVICE_ID, .part_no = SJA1105S_PART_NO, .static_ops = sja1105s_table_ops, diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index ff3fe471efc2..139b7b4fbd0d 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -838,12 +838,7 @@ sja1105_static_config_get_length(const struct sja1105_static_config *config) /* Compatibility matrices */ /* SJA1105E: First generation, no TTEthernet */ -struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105et_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -874,9 +869,6 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105et_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -916,7 +908,7 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { }; /* SJA1105T: First generation, TTEthernet */ -struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), @@ -1034,12 +1026,7 @@ struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { }; /* SJA1105P: Second generation, no TTEthernet, no SGMII */ -struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105pqrs_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -1070,9 +1057,6 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105pqrs_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -1112,7 +1096,7 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { }; /* SJA1105Q: Second generation, TTEthernet, no SGMII */ -struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), @@ -1230,12 +1214,7 @@ struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { }; /* SJA1105R: Second generation, no TTEthernet, SGMII */ -struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105pqrs_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -1266,9 +1245,6 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105pqrs_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -1308,7 +1284,7 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { }; /* SJA1105S: Second generation, TTEthernet, SGMII */ -struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h index ee0f10062763..bc7606899289 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.h +++ b/drivers/net/dsa/sja1105/sja1105_static_config.h @@ -381,12 +381,12 @@ struct sja1105_static_config { struct sja1105_table tables[BLK_IDX_MAX]; }; -extern struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX]; size_t sja1105_table_header_packing(void *buf, void *hdr, enum packing_op op); void diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c index 3aa1a8b5f766..31d8acff1f01 100644 --- a/drivers/net/dsa/sja1105/sja1105_tas.c +++ b/drivers/net/dsa/sja1105/sja1105_tas.c @@ -475,8 +475,7 @@ bool sja1105_gating_check_conflicts(struct sja1105_private *priv, int port, if (list_empty(&gating_cfg->entries)) return false; - dummy = kzalloc(sizeof(struct tc_taprio_sched_entry) * num_entries + - sizeof(struct tc_taprio_qopt_offload), GFP_KERNEL); + dummy = kzalloc(struct_size(dummy, entries, num_entries), GFP_KERNEL); if (!dummy) { NL_SET_ERR_MSG_MOD(extack, "Failed to allocate memory"); return true; diff --git a/drivers/net/dsa/sja1105/sja1105_vl.c b/drivers/net/dsa/sja1105/sja1105_vl.c index af3565160db6..ffc4042b4502 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.c +++ b/drivers/net/dsa/sja1105/sja1105_vl.c @@ -778,7 +778,7 @@ int sja1105_vl_stats(struct sja1105_private *priv, int port, pkts = timingerr + unreleased + lengtherr; - flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, + flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0, jiffies - rule->vl.stats.lastused, FLOW_ACTION_HW_STATS_IMMEDIATE); diff --git a/drivers/net/dsa/vitesse-vsc73xx-platform.c b/drivers/net/dsa/vitesse-vsc73xx-platform.c index 5e54a5726aa4..2a57f337b2a2 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-platform.c +++ b/drivers/net/dsa/vitesse-vsc73xx-platform.c @@ -28,7 +28,7 @@ #define VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK 0xf #define VSC73XX_CMD_PLATFORM_REGISTER_SHIFT 2 -/** +/* * struct vsc73xx_platform - VSC73xx Platform state container */ struct vsc73xx_platform { diff --git a/drivers/net/dsa/vitesse-vsc73xx-spi.c b/drivers/net/dsa/vitesse-vsc73xx-spi.c index e73c8fcddc9f..81eca4a5781d 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-spi.c +++ b/drivers/net/dsa/vitesse-vsc73xx-spi.c @@ -26,7 +26,7 @@ #define VSC73XX_CMD_SPI_BLOCK_MASK 0x7 #define VSC73XX_CMD_SPI_SUBBLOCK_MASK 0xf -/** +/* * struct vsc73xx_spi - VSC73xx SPI state container */ struct vsc73xx_spi { |