/* Flow Control registers indexed by port number */ staticunsignedint flow_ctl_reg[] = {
LAN9303_MANUAL_FC_0,
LAN9303_MANUAL_FC_1,
LAN9303_MANUAL_FC_2
};
/* we can lose arbitration for the I2C case, because the device * tries to detect and read an external EEPROM after reset and acts as * a master on the shared I2C bus itself. This conflicts with our * attempts to access the device as a slave at the same moment.
*/ for (i = 0; i < 5; i++) {
ret = regmap_read(regmap, offset, reg); if (!ret) return 0; if (ret != -EAGAIN) break;
msleep(500);
}
return -EIO;
}
staticint lan9303_read_wait(struct lan9303 *chip, int offset, u32 mask)
{ int i;
for (i = 0; i < 25; i++) {
u32 reg; int ret;
ret = lan9303_read(chip->regmap, offset, ®); if (ret) {
dev_err(chip->dev, "%s failed to read offset %d: %d\n",
__func__, offset, ret); return ret;
} if (!(reg & mask)) return 0;
usleep_range(1000, 2000);
}
return -ETIMEDOUT;
}
staticint lan9303_virt_phy_reg_read(struct lan9303 *chip, int regnum)
{ int ret;
u32 val;
if (regnum > MII_EXPANSION) return -EINVAL;
ret = lan9303_read(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, &val); if (ret) return ret;
return val & 0xffff;
}
staticint lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val)
{ if (regnum > MII_EXPANSION) return -EINVAL;
ret = lan9303_switch_wait_for_completion(chip); if (ret) goto on_error;
ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_DATA, val); if (ret) {
dev_err(chip->dev, "Failed to write csr data reg: %d\n", ret); goto on_error;
}
/* trigger write */
ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_CMD, reg); if (ret)
dev_err(chip->dev, "Failed to write csr command reg: %d\n",
ret);
staticint lan9303_detect_phy_setup(struct lan9303 *chip)
{ int reg;
/* Calculate chip->phy_addr_base: * Depending on the 'phy_addr_sel_strap' setting, the three phys are * using IDs 0-1-2 or IDs 1-2-3. We cannot read back the * 'phy_addr_sel_strap' setting directly, so we need a test, which * configuration is active: * Special reg 18 of phy 3 reads as 0x0000, if 'phy_addr_sel_strap' is 0 * and the IDs are 0-1-2, else it contains something different from * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3. * 0xffff is returned on MDIO read with no response.
*/
reg = chip->ops->phy_read(chip, 3, MII_LAN911X_SPECIAL_MODES); if (reg < 0) {
dev_err(chip->dev, "Failed to detect phy config: %d\n", reg); return reg;
}
/* learned entries has only one port, we can just delete */
dat1 &= ~LAN9303_ALR_DAT1_VALID; /* delete entry */
lan9303_alr_make_entry_raw(chip, dat0, dat1);
return 0;
}
struct port_fdb_dump_ctx { int port; void *data;
dsa_fdb_dump_cb_t *cb;
};
/* Delete static port from ALR entry, delete entry if last port */ staticint lan9303_alr_del_port(struct lan9303 *chip, const u8 *mac, int port)
{ struct lan9303_alr_cache_entry *entr;
mutex_lock(&chip->alr_mutex);
entr = lan9303_alr_cache_find_mac(chip, mac); if (!entr) goto out; /* no static entry found */
entr->port_map &= ~BIT(port); if (entr->port_map == 0) /* zero means its free again */
eth_zero_addr(entr->mac_addr);
lan9303_alr_set_entry(chip, mac, entr->port_map, entr->stp_override);
out:
mutex_unlock(&chip->alr_mutex); return 0;
}
staticint lan9303_disable_processing_port(struct lan9303 *chip, unsignedint port)
{ int ret;
/* disable RX, but keep register reset default values else */
ret = lan9303_write_switch_port(chip, port, LAN9303_MAC_RX_CFG_0,
LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES); if (ret) return ret;
/* forward special tagged packets from port 0 to port 1 *or* port 2 */ staticint lan9303_setup_tagging(struct lan9303 *chip)
{ int ret;
u32 val; /* enable defining the destination port via special VLAN tagging * for port 0
*/
ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE,
LAN9303_SWE_INGRESS_PORT_TYPE_VLAN); if (ret) return ret;
/* tag incoming packets at port 1 and 2 on their way to port 0 to be * able to discover their source port
*/
val = LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0; return lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE, val);
}
/* We want a special working switch: * - do not forward packets between port 1 and 2 * - forward everything from port 1 to port 0 * - forward everything from port 2 to port 0
*/ staticint lan9303_separate_ports(struct lan9303 *chip)
{ int ret;
lan9303_alr_del_port(chip, eth_stp_addr, 0);
ret = lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR,
LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 |
LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 |
LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT2 |
LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING |
LAN9303_SWE_PORT_MIRROR_SNIFF_ALL); if (ret) return ret;
/* prevent port 1 and 2 from forwarding packets by their own */ return lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE,
LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 |
LAN9303_SWE_PORT_STATE_BLOCKING_PORT1 |
LAN9303_SWE_PORT_STATE_BLOCKING_PORT2);
}
staticvoid lan9303_handle_reset(struct lan9303 *chip)
{ if (!chip->reset_gpio) return;
gpiod_set_value_cansleep(chip->reset_gpio, 1);
if (chip->reset_duration != 0)
msleep(chip->reset_duration);
/* release (deassert) reset and activate the device */
gpiod_set_value_cansleep(chip->reset_gpio, 0);
}
/* stop processing packets for all ports */ staticint lan9303_disable_processing(struct lan9303 *chip)
{ int p;
for (p = 1; p < LAN9303_NUM_PORTS; p++) { int ret = lan9303_disable_processing_port(chip, p);
if (ret) return ret;
}
return 0;
}
staticint lan9303_check_device(struct lan9303 *chip)
{ int ret; int err;
u32 reg;
/* In I2C-managed configurations this polling loop will clash with * switch's reading of EEPROM right after reset and this behaviour is * not configurable. While lan9303_read() already has quite long retry * timeout, seems not all cases are being detected as arbitration error. * * According to datasheet, EEPROM loader has 30ms timeout (in case of * missing EEPROM). * * Loading of the largest supported EEPROM is expected to take at least * 5.9s.
*/
err = read_poll_timeout(lan9303_read, ret,
!ret && reg & LAN9303_HW_CFG_READY,
20000, 6000000, false,
chip->regmap, LAN9303_HW_CFG, ®); if (ret) {
dev_err(chip->dev, "failed to read HW_CFG reg: %pe\n",
ERR_PTR(ret)); return ret;
} if (err) {
dev_err(chip->dev, "HW_CFG not ready: 0x%08x\n", reg); return err;
}
ret = lan9303_read(chip->regmap, LAN9303_CHIP_REV, ®); if (ret) {
dev_err(chip->dev, "failed to read chip revision register: %d\n",
ret); return ret;
}
/* The default state of the LAN9303 device is to forward packets between * all ports (if not configured differently by an external EEPROM). * The initial state of a DSA device must be forwarding packets only * between the external and the internal ports and no forwarding * between the external ports. In preparation we stop packet handling * at all for now until the LAN9303 device is re-programmed accordingly.
*/
ret = lan9303_disable_processing(chip); if (ret)
dev_warn(chip->dev, "failed to disable switching %d\n", ret);
/* Make sure that port 0 is the cpu port */ if (!dsa_is_cpu_port(ds, 0)) {
dev_err(chip->dev, "port 0 is not the CPU port\n"); return -EINVAL;
}
/* Virtual Phy: Remove Turbo 200Mbit mode */
ret = lan9303_read(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, ®); if (ret) return (ret);
/* Clear the TURBO Mode bit if it was set. */ if (reg & LAN9303_VIRT_SPECIAL_TURBO) {
reg &= ~LAN9303_VIRT_SPECIAL_TURBO;
regmap_write(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, reg);
}
ret = lan9303_setup_tagging(chip); if (ret)
dev_err(chip->dev, "failed to setup port tagging %d\n", ret);
ret = lan9303_separate_ports(chip); if (ret)
dev_err(chip->dev, "failed to separate ports %d\n", ret);
ret = lan9303_enable_processing_port(chip, 0); if (ret)
dev_err(chip->dev, "failed to re-enable switching %d\n", ret);
/* Trap IGMP to port 0 */
ret = lan9303_write_switch_reg_mask(chip, LAN9303_SWE_GLB_INGRESS_CFG,
LAN9303_SWE_GLB_INGR_IGMP_TRAP |
LAN9303_SWE_GLB_INGR_IGMP_PORT(0),
LAN9303_SWE_GLB_INGR_IGMP_PORT(1) |
LAN9303_SWE_GLB_INGR_IGMP_PORT(2)); if (ret)
dev_err(chip->dev, "failed to setup IGMP trap %d\n", ret);
return 0;
}
struct lan9303_mib_desc { unsignedint offset; /* offset of first MAC */ constchar *name;
};
if (chip->is_bridged)
lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE,
chip->swe_port_state); /* else: touching SWE_PORT_STATE would break port separation */
}
/* On this device, we are only interested in doing something here if * this is the xMII port. All other ports are 10/100 phys using MDIO * to control there link settings.
*/ if (!IS_PORT_XMII(port)) return;
/* Disable auto-negotiation and force the speed/duplex settings. */
ctl = lan9303_phy_read(ds, port, MII_BMCR);
ctl &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX); if (speed == SPEED_100)
ctl |= BMCR_SPEED100; if (duplex == DUPLEX_FULL)
ctl |= BMCR_FULLDPLX;
lan9303_phy_write(ds, port, MII_BMCR, ctl);
/* Force the flow control settings. */
lan9303_read(chip->regmap, flow_ctl_reg[port], ®);
reg &= ~(LAN9303_BP_EN | LAN9303_RX_FC_EN | LAN9303_TX_FC_EN); if (rx_pause)
reg |= (LAN9303_RX_FC_EN | LAN9303_BP_EN); if (tx_pause)
reg |= LAN9303_TX_FC_EN;
regmap_write(chip->regmap, flow_ctl_reg[port], reg);
}
ret = lan9303_probe_reset_gpio(chip, np); if (ret) return ret;
lan9303_handle_reset(chip);
/* First read to the device. This is a Dummy read to ensure MDIO */ /* access is in 32-bit sync. */
ret = lan9303_read(chip->regmap, LAN9303_BYTE_ORDER, ®); if (ret) {
dev_err(chip->dev, "failed to access the device: %d\n",
ret); if (!chip->reset_gpio) {
dev_dbg(chip->dev, "hint: maybe failed due to missing reset GPIO\n");
} return ret;
}
ret = lan9303_check_device(chip); if (ret) return ret;
ret = lan9303_register_switch(chip); if (ret) {
dev_dbg(chip->dev, "Failed to register switch: %d\n", ret); return ret;
}
return 0;
}
EXPORT_SYMBOL(lan9303_probe);
int lan9303_remove(struct lan9303 *chip)
{ int rc;
rc = lan9303_disable_processing(chip); if (rc != 0)
dev_warn(chip->dev, "shutting down failed\n");
dsa_unregister_switch(chip->ds);
/* assert reset to the whole device to prevent it from doing anything */
gpiod_set_value_cansleep(chip->reset_gpio, 1);
MODULE_AUTHOR("Juergen Borleis <kernel@pengutronix.de>");
MODULE_DESCRIPTION("Core driver for SMSC/Microchip LAN9303 three port ethernet switch");
MODULE_LICENSE("GPL v2");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.