From: Markus Stockhausen Date: Wed, 3 Sep 2025 08:16:31 +0000 (-0400) Subject: realtek: carve out mdio bus from ethernet driver X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F19942%2Fhead;p=thirdparty%2Fopenwrt.git realtek: carve out mdio bus from ethernet driver So much code was distributed between phy/ethernet/dsa drivers. A lot was already cleand up before. With this step the mdio bus gets its own space and is no longer hidden inside the ethernet driver. This commit is mostly a copy/paste that includes only minor changes. - define prefixes are renamed to RTMDIO - The driver is totally self contained (does not rely on SoC include) - The DTS structure (mdio node below ethernet node) was kept - The driver is added to the kernel config of all subtargets. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/19942 Signed-off-by: Robert Marko --- diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c index d2d4d0bf7c8..eb7bdcfc950 100644 --- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c @@ -27,13 +27,6 @@ extern struct rtl83xx_soc_info soc_info; extern int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data); -extern int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); -extern int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val); -extern int rtl931x_read_sds_phy(int phy_addr, int page, int phy_reg); -extern int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val); -extern int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val); -extern int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); - /* Maximum number of RX rings is 8 on RTL83XX and 32 on the 93XX * The ring is assigned by switch based on packet/port priortity * Maximum number of TX rings is 2, Ring 2 being the high priority @@ -59,24 +52,6 @@ extern int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); #define WRAP 0x2 #define RING_BUFFER 1600 -#define RTMDIO_MAX_PORT 57 -#define RTMDIO_MAX_SMI_BUS 4 -#define RTMDIO_PAGE_SELECT 0x1f -#define RTMDIO_PORT_SELECT 0x2000 - -#define RTMDIO_READ BIT(0) -#define RTMDIO_WRITE BIT(1) -#define RTMDIO_ABS BIT(2) -#define RTMDIO_PKG BIT(3) - -/* MDIO SerDes registers */ -#define RTMDIO_838X_BASE (0xe780) -#define RTMDIO_839X_BASE (0xa000) -#define RTMDIO_930X_SDS_INDACS_CMD (0x03B0) -#define RTMDIO_930X_SDS_INDACS_DATA (0x03B4) -#define RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL (0x5638) -#define RTMDIO_931X_SERDES_INDRT_DATA_CTRL (0x563C) - struct p_hdr { uint8_t *buf; uint16_t reserved; @@ -1521,1848 +1496,227 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev, return phylink_ethtool_ksettings_set(priv->phylink, cmd); } -/* - * On all Realtek switch platforms the hardware periodically reads the link status of all - * PHYs. This is to some degree programmable, so that one can tell the hardware to read - * specific C22 registers from specific pages, or C45 registers, to determine the current - * link speed, duplex, flow-control, ... - * - * This happens without any need for the driver to do anything at runtime, completely - * invisible and in a parallel hardware thread, independent of the CPU running Linux. - * All one needs to do is to set it up once. Having the MAC link settings automatically - * follow the PHY link status also happens to be the only way to control MAC port status - * in a meaningful way, or at least it's the only way we fully understand, as this is - * what every vendor firmware is doing. - * - * The hardware PHY polling unit doesn't care about bus locking, it just assumes that all - * paged PHY operations are also done via the same hardware unit offering this PHY access - * abstractions. - * - * Additionally at least the RTL838x and RTL839x devices are known to have a so called - * raw mode. Using the special MAX_PAGE-1 with the MDIO controller found in Realtek - * SoCs allows to access the PHY in raw mode, ie. bypassing the cache and paging engine - * of the MDIO controller. E.g. for RTL838x this is 0xfff. - * - * On the other hand Realtek PHYs usually make use of select register 0x1f to switch - * pages. There is no problem to issue separate page and access bus calls to the PHYs - * when they are not attached to an Realtek SoC. The paradigm should be to keep the PHY - * implementation bus independent. - * - * As if this is not enough the PHY packages consist of 4 or 8 ports that all can be - * programmed individually. Some registers are only available on port 0 and configure - * the whole package. - * - * To bring all this together we need a tricky bus design that intercepts select page - * calls but lets raw page accesses through. And especially knows how to handle raw - * accesses to the select register. Additionally we need the possibility to write to - * all 8 ports of the PHY individually. - * - * While the C45 clause stuff is pretty standard the legacy functions basically track - * the accesses and the state of the bus with the attributes page[], raw[] and portaddr - * of the bus_priv structure. The page selection works as follows: - * - * phy_write(phydev, RTMDIO_PAGE_SELECT, 12) : store internal page 12 in driver - * phy_write(phydev, 7, 33) : write page=12, reg=7, val=33 - * - * or simply - * - * phy_write_paged(phydev, 12, 7, 33) : write page=12, reg=7, val=33 - * - * The port selection works as follows and must be called under a held mdio bus lock - * - * __mdiobus_write(bus, RTMDIO_PORT_SELECT, 4) : switch to port 4 - * __phy_write(phydev, RTMDIO_PAGE_SELECT, 11) : store internal page 11 in driver - * __phy_write(phydev, 8, 19) : write page=11, reg=8, val=19, port=4 - * - * Any Realtek PHY that will be connected to this bus must simply provide the standard - * page functions: - * - * define RTL821X_PAGE_SELECT 0x1f - * - * static int rtl821x_read_page(struct phy_device *phydev) - * { - * return __phy_read(phydev, RTL821X_PAGE_SELECT); - * } - * - * static int rtl821x_write_page(struct phy_device *phydev, int page) - * { - * return __phy_write(phydev, RTL821X_PAGE_SELECT, page); - * } - * - * In case there are non Realtek PHYs attached to the bus the logic might need to be - * reimplemented. For now it should be sufficient. - */ - -DEFINE_MUTEX(rtmdio_lock); -DEFINE_MUTEX(rtmdio_lock_sds); - -struct rtmdio_bus_priv { - u16 id; - u16 family_id; - int extaddr; - int rawpage; - int cpu_port; - int page[RTMDIO_MAX_PORT]; - bool raw[RTMDIO_MAX_PORT]; - int smi_bus[RTMDIO_MAX_PORT]; - u8 smi_addr[RTMDIO_MAX_PORT]; - int sds_id[RTMDIO_MAX_PORT]; - bool smi_bus_isc45[RTMDIO_MAX_SMI_BUS]; - bool phy_is_internal[RTMDIO_MAX_PORT]; - phy_interface_t interfaces[RTMDIO_MAX_PORT]; - int (*read_mmd_phy)(u32 port, u32 addr, u32 reg, u32 *val); - int (*write_mmd_phy)(u32 port, u32 addr, u32 reg, u32 val); - int (*read_phy)(u32 port, u32 page, u32 reg, u32 *val); - int (*write_phy)(u32 port, u32 page, u32 reg, u32 val); - int (*read_sds_phy)(int sds, int page, int regnum); - int (*write_sds_phy)(int sds, int page, int regnum, u16 val); -}; - -/* - * Provide a generic read/write function so we can access arbitrary ports on the bus. - * E.g. other ports of a PHY package on the bus. This basically resembles the kernel - * phy_read_paged() and phy_write_paged() functions. To inform the bus that we are - * working on a not default port send a RTMDIO_PORT_SELECT command at the beginning - * and the end to switch the port handling logic. - */ - -static int rtmdio_access(struct phy_device *phydev, int op, int port, - int page, u32 regnum, u16 val) -{ - int r, ret = 0, oldpage; - - if (op & RTMDIO_PKG) { - if (!phydev->shared) - return -EIO; - port = phydev->shared->base_addr + port; - } - - /* lock and inform bus about non default addressing */ - phy_lock_mdio_bus(phydev); - __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, - RTMDIO_PORT_SELECT, port); - - oldpage = ret = __phy_read(phydev, RTMDIO_PAGE_SELECT); - if (oldpage >= 0 && oldpage != page) { - ret = __phy_write(phydev, RTMDIO_PAGE_SELECT, page); - if (ret < 0) - oldpage = ret; - } - - if (oldpage >= 0) { - if (op & RTMDIO_WRITE) - ret = __phy_write(phydev, regnum, val); - else - ret = __phy_read(phydev, regnum); - } - - if (oldpage >= 0) { - r = __phy_write(phydev, RTMDIO_PAGE_SELECT, oldpage); - if (ret >= 0 && r < 0) - ret = r; - } else - ret = oldpage; - - /* reset bus to default adressing and unlock it */ - __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, - RTMDIO_PORT_SELECT, -1); - phy_unlock_mdio_bus(phydev); - - return ret; -} - -/* - * To make use of the shared package functions provide wrappers that align with kernel - * naming conventions. The package() functions are useful to change settings on the - * package as a whole. The package_port() functions will allow to target the PHYs - * of a package individually. The port() only functions allow to access arbitrary ports - * on the bus through a PHY. - */ - -int phy_package_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val) -{ - return rtmdio_access(phydev, RTMDIO_WRITE | RTMDIO_PKG, port, page, regnum, val); -} - -int phy_package_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) -{ - return rtmdio_access(phydev, RTMDIO_WRITE | RTMDIO_PKG, 0, page, regnum, val); -} - -int phy_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val) -{ - return rtmdio_access(phydev, RTMDIO_WRITE | RTMDIO_ABS, port, page, regnum, val); -} - -int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum) +static int rtl931x_chip_init(struct rtl838x_eth_priv *priv) { - return rtmdio_access(phydev, RTMDIO_READ | RTMDIO_PKG, port, page, regnum, 0); -} + pr_info("In %s\n", __func__); -int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum) -{ - return rtmdio_access(phydev, RTMDIO_READ | RTMDIO_PKG, 0, page, regnum, 0); -} + /* Initialize Encapsulation memory and wait until finished */ + sw_w32(0x1, RTL931X_MEM_ENCAP_INIT); + do { } while (sw_r32(RTL931X_MEM_ENCAP_INIT) & 1); + pr_info("%s: init ENCAP done\n", __func__); -int phy_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum) -{ - return rtmdio_access(phydev, RTMDIO_READ | RTMDIO_ABS, port, page, regnum, 0); -} + /* Initialize Managemen Information Base memory and wait until finished */ + sw_w32(0x1, RTL931X_MEM_MIB_INIT); + do { } while (sw_r32(RTL931X_MEM_MIB_INIT) & 1); + pr_info("%s: init MIB done\n", __func__); -/* SerDes reader/writer functions for the ports without external phy. */ + /* Initialize ACL (PIE) memory and wait until finished */ + sw_w32(0x1, RTL931X_MEM_ACL_INIT); + do { } while (sw_r32(RTL931X_MEM_ACL_INIT) & 1); + pr_info("%s: init ACL done\n", __func__); -/* - * The RTL838x has 6 SerDes. The 16 bit registers start at 0xbb00e780 and are mapped directly into - * 32 bit memory addresses. High 16 bits are always empty. A "lower" memory block serves pages 0/3 - * a "higher" memory block pages 1/2. - */ + /* Initialize ALE memory and wait until finished */ + sw_w32(0xFFFFFFFF, RTL931X_MEM_ALE_INIT_0); + do { } while (sw_r32(RTL931X_MEM_ALE_INIT_0)); + sw_w32(0x7F, RTL931X_MEM_ALE_INIT_1); + sw_w32(0x7ff, RTL931X_MEM_ALE_INIT_2); + do { } while (sw_r32(RTL931X_MEM_ALE_INIT_2) & 0x7ff); + pr_info("%s: init ALE done\n", __func__); -static int rtmdio_838x_reg_offset(int sds, int page, int regnum) -{ - if (sds < 0 || sds > 5) - return -EINVAL; + /* Enable ESD auto recovery */ + sw_w32(0x1, RTL931X_MDX_CTRL_RSVD); - if (page == 0 || page == 3) - return (sds << 9) + (page << 7) + (regnum << 2); - else if (page == 1 || page == 2) - return 0xb80 + (sds << 8) + (page << 7) + (regnum << 2); + /* Init SPI, is this for thermal control or what? */ + sw_w32_mask(0x7 << 11, 0x2 << 11, RTL931X_SPI_CTRL0); - return -EINVAL; + return 0; } -static int rtmdio_838x_read_sds_phy(int sds, int page, int regnum) +static netdev_features_t rtl838x_fix_features(struct net_device *dev, + netdev_features_t features) { - int offset = rtmdio_838x_reg_offset(sds, page, regnum); - - if (offset < 0) - return offset; - - return sw_r32(RTMDIO_838X_BASE + offset) & GENMASK(15, 0); + return features; } -static int rtmdio_838x_write_sds_phy(int sds, int page, int regnum, u16 val) +static int rtl83xx_set_features(struct net_device *dev, netdev_features_t features) { - int offset = rtmdio_838x_reg_offset(sds, page, regnum); - - if (offset < 0) - return offset; + struct rtl838x_eth_priv *priv = netdev_priv(dev); - sw_w32(val, RTMDIO_838X_BASE + offset); + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(3), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); + } return 0; } -/* RTL838x specific MDIO functions */ - -static int rtmdio_838x_smi_wait_op(int timeout) -{ - int ret = 0; - u32 val; - - ret = readx_poll_timeout(sw_r32, RTL838X_SMI_ACCESS_PHY_CTRL_1, - val, !(val & 0x1), 20, timeout); - if (ret) - pr_err("%s: timeout\n", __func__); - - return ret; -} - -/* Reads a register in a page from the PHY */ -int rtmdio_838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +static int rtl93xx_set_features(struct net_device *dev, netdev_features_t features) { - u32 v, park_page = 0x1f << 15; - int err; + struct rtl838x_eth_priv *priv = netdev_priv(dev); - if (port > 31) { - *val = 0xffff; - return 0; + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(4), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); } - if (page > 4095 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); - v = reg << 20 | page << 3; - sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); - sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); - - err = rtmdio_838x_smi_wait_op(100000); - if (err) - goto errout; - - *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; -errout: - mutex_unlock(&rtmdio_lock); - - return err; + return 0; } -/* Write to a register in a page of the PHY */ -int rtmdio_838x_write_phy(u32 port, u32 page, u32 reg, u32 val) +static struct phylink_pcs *rtl838x_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) { - u32 v, park_page = 0x1f << 15; - int err; - - val &= 0xffff; - if (port > 31 || page > 4095 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - sw_w32(BIT(port), RTL838X_SMI_ACCESS_PHY_CTRL_0); - sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); - - v = reg << 20 | page << 3 | 0x4; - sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); - sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); - - err = rtmdio_838x_smi_wait_op(100000); - mutex_unlock(&rtmdio_lock); + struct net_device *dev = to_net_dev(config->dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); - return err; + return &priv->pcs; } -/* Read an mmd register of a PHY */ -static int rtmdio_838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) -{ - int err; - u32 v; - - mutex_lock(&rtmdio_lock); +static const struct net_device_ops rtl838x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; - sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); - sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); +static const struct net_device_ops rtl839x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl839x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; - v = addr << 16 | reg; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3); +static const struct net_device_ops rtl930x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl930x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; - /* mmd-access | read | cmd-start */ - v = 1 << 1 | 0 << 2 | 1; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); +static const struct net_device_ops rtl931x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl931x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, +}; - err = rtmdio_838x_smi_wait_op(100000); - if (err) - goto errout; +static const struct phylink_pcs_ops rtl838x_pcs_ops = { + .pcs_get_state = rtl838x_pcs_get_state, + .pcs_an_restart = rtl838x_pcs_an_restart, + .pcs_config = rtl838x_pcs_config, +}; - *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; -errout: - mutex_unlock(&rtmdio_lock); +static const struct phylink_mac_ops rtl838x_phylink_ops = { + .mac_select_pcs = rtl838x_mac_select_pcs, + .mac_config = rtl838x_mac_config, + .mac_link_down = rtl838x_mac_link_down, + .mac_link_up = rtl838x_mac_link_up, +}; - return err; -} +static const struct ethtool_ops rtl838x_ethtool_ops = { + .get_link_ksettings = rtl838x_get_link_ksettings, + .set_link_ksettings = rtl838x_set_link_ksettings, +}; -/* Write to an mmd register of a PHY */ -static int rtmdio_838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) +static int __init rtl838x_eth_probe(struct platform_device *pdev) { - int err; - u32 v; - - pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); - val &= 0xffff; - mutex_lock(&rtmdio_lock); - - sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); - sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); - sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3); - sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3); - /* mmd-access | write | cmd-start */ - v = 1 << 1 | 1 << 2 | 1; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + struct net_device *dev; + struct device_node *dn = pdev->dev.of_node; + struct rtl838x_eth_priv *priv; + struct resource *res, *mem; + phy_interface_t phy_mode; + struct phylink *phylink; + u8 mac_addr[ETH_ALEN]; + int err = 0, rxrings, rxringlen; + struct ring_b *ring; - err = rtmdio_838x_smi_wait_op(100000); - mutex_unlock(&rtmdio_lock); + pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n", + (u32)pdev, (u32)(&(pdev->dev))); - return err; -} + if (!dn) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } -/* - * The RTL839x has 14 SerDes starting at 0xbb00a000. 0-7, 10, 11 are 5GBit, 8, 9, 12, 13 are - * 10 GBit. Two adjacent SerDes are tightly coupled and share a 1024 bytes register area. Per 32 - * bit address two registers are stored. The first register is stored in the lower 2 bytes ("on - * the right" due to big endian) and the second register in the upper 2 bytes. The following - * register areas are known: - * - * - XSG0 (4 pages @ offset 0x000): for even SerDes - * - XSG1 (4 pages @ offset 0x100): for odd SerDes - * - TGRX (4 pages @ offset 0x200): for even 10G SerDes - * - ANA_RG (2 pages @ offset 0x300): for even 5G SerDes - * - ANA_RG (2 pages @ offset 0x380): for odd 5G SerDes - * - ANA_TG (2 pages @ offset 0x300): for even 10G SerDes - * - ANA_TG (2 pages @ offset 0x380): for odd 10G SerDes - * - * The most consistent mapping that aligns to the RTL93xx devices is: - * - * even 5G SerDes odd 5G SerDes even 10G SerDes odd 10G SerDes - * Page 0: XSG0/0 XSG1/0 XSG0/0 XSG1/0 - * Page 1: XSG0/1 XSG1/1 XSG0/1 XSG1/1 - * Page 2: XSG0/2 XSG1/2 XSG0/2 XSG1/2 - * Page 3: XSG0/3 XSG1/3 XSG0/3 XSG1/3 - * Page 4: TGRX/0 - * Page 5: TGRX/1 - * Page 6: TGRX/2 - * Page 7: TGRX/3 - * Page 8: ANA_RG ANA_RG - * Page 9: ANA_RG_EXT ANA_RG_EXT - * Page 10: ANA_TG ANA_TG - * Page 11: ANA_TG_EXT ANA_TG_EXT - */ + rxrings = (soc_info.family == RTL8380_FAMILY_ID + || soc_info.family == RTL8390_FAMILY_ID) ? 8 : 32; + rxrings = rxrings > MAX_RXRINGS ? MAX_RXRINGS : rxrings; + rxringlen = MAX_ENTRIES / rxrings; + rxringlen = rxringlen > MAX_RXLEN ? MAX_RXLEN : rxringlen; -static int rtmdio_839x_reg_offset(int sds, int page, int regnum) -{ - int offset = ((sds & 0xfe) << 9) + ((regnum & 0xfe) << 1) + (page << 6); - int sds5g = (GENMASK(11, 10) | GENMASK(7, 0)) & BIT(sds); - - if (sds < 0 || sds > 13 || page < 0 || page > 11 || regnum < 0 || regnum > 31) - return -EIO; - - if (page < 4) - return offset + ((sds & 1) << 8); - else if ((page & 4) && (sds == 8 || sds == 12)) - return offset + 0x100; - else if (page >= 8 && page <= 9 && sds5g) - return offset + 0x100 + ((sds & 1) << 7); - else if (page >= 10 && !sds5g) - return offset + 0x80 + ((sds & 1) << 7); - - return -EINVAL; /* hole */ -} + dev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(struct rtl838x_eth_priv), TXRINGS, rxrings); + if (!dev) + return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); + priv = netdev_priv(dev); -static int rtmdio_839x_read_sds_phy(int sds, int page, int regnum) -{ - int bitpos = ((regnum << 4) & 0x10); - int offset; - u32 val; + /* obtain buffer memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + mem = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), res->name); + if (!mem) { + dev_err(&pdev->dev, "cannot request memory space\n"); + return -ENXIO; + } - offset = rtmdio_839x_reg_offset(sds, page, regnum); - if (offset == -EINVAL) - return 0; + dev->mem_start = mem->start; + dev->mem_end = mem->end; + } else { + dev_err(&pdev->dev, "cannot request IO resource\n"); + return -ENXIO; + } - if (offset < 0) - return offset; + /* Allocate buffer memory */ + priv->membase = dmam_alloc_coherent(&pdev->dev, rxrings * rxringlen * RING_BUFFER + + sizeof(struct ring_b) + sizeof(struct notify_b), + (void *)&dev->mem_start, GFP_KERNEL); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); + return -ENOMEM; + } - /* phy id is empty so simulate one */ - if (page == 2 && regnum == 2) - return 0x1c; - if (page == 2 && regnum == 3) - return 0x8393; + /* Allocate ring-buffer space at the end of the allocated memory */ + ring = priv->membase; + ring->rx_space = priv->membase + sizeof(struct ring_b) + sizeof(struct notify_b); - val = sw_r32(RTMDIO_839X_BASE + offset); - val = (val >> bitpos) & 0xffff; + spin_lock_init(&priv->lock); - return val; -} - -static int rtmdio_839x_write_sds_phy(int sds, int page, int regnum, u16 val) -{ - u32 neighbor; - int offset; - u32 set; - - offset = rtmdio_839x_reg_offset(sds, page, regnum); - if (offset == -EINVAL) - return 0; - - if (offset < 0) - return 0; - - neighbor = rtmdio_839x_read_sds_phy(sds, page, regnum ^ 1); - if (regnum & 1) - set = (val << 16) + neighbor; - else - set = (neighbor << 16) + val; - - sw_w32(set, RTMDIO_839X_BASE + offset); - - return 0; -} - -/* RTL839x specific MDIO functions */ - -static int rtmdio_839x_smi_wait_op(int timeout) -{ - int ret = 0; - u32 val; - - ret = readx_poll_timeout(sw_r32, RTL839X_PHYREG_ACCESS_CTRL, - val, !(val & 0x1), 20, timeout); - if (ret) - pr_err("%s: timeout\n", __func__); - - return ret; -} - -static int rtmdio_839x_read_phy(u32 port, u32 page, u32 reg, u32 *val) -{ - int err = 0; - u32 v; - - if (port >= RTL839X_CPU_PORT || page > 8191 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - sw_w32_mask(0xffff0000, port << 16, RTL839X_PHYREG_DATA_CTRL); - v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - sw_w32(0x1ff, RTL839X_PHYREG_CTRL); - - v |= 1; - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - err = rtmdio_839x_smi_wait_op(100000); - if (err) - goto errout; - - *val = sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff; - -errout: - mutex_unlock(&rtmdio_lock); - - return err; -} - -static int rtmdio_839x_write_phy(u32 port, u32 page, u32 reg, u32 val) -{ - int err = 0; - u32 v; - - val &= 0xffff; - if (port >= RTL839X_CPU_PORT || page > 8191 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access */ - sw_w32(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); - sw_w32(BIT_ULL(port) >> 32, RTL839X_PHYREG_PORT_CTRL + 4); - - sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL); - - v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - sw_w32(0x1ff, RTL839X_PHYREG_CTRL); - - v |= BIT(3) | 1; /* Write operation and execute */ - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - err = rtmdio_839x_smi_wait_op(100000); - if (err) - goto errout; - - if (sw_r32(RTL839X_PHYREG_ACCESS_CTRL) & 0x2) - err = -EIO; - -errout: - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* Read an mmd register of the PHY */ -static int rtmdio_839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) -{ - int err = 0; - u32 v; - - /* Take bug on RTL839x Rev <= C into account */ - if (port >= RTL839X_CPU_PORT) - return -EIO; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access */ - sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); - - v = BIT(2) | BIT(0); /* MMD-access | EXEC */ - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - err = rtmdio_839x_smi_wait_op(100000); - if (err) - goto errout; - - /* There is no error-checking via BIT 1 of v, as it does not seem to be set correctly */ - *val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff); - pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); - -errout: - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* Write to an mmd register of the PHY */ -static int rtmdio_839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) -{ - int err = 0; - u32 v; - - /* Take bug on RTL839x Rev <= C into account */ - if (port >= RTL839X_CPU_PORT) - return -EIO; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access */ - sw_w32(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); - sw_w32(BIT_ULL(port) >> 32, RTL839X_PHYREG_PORT_CTRL + 4); - - /* Set data to write */ - sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); - - v = BIT(3) | BIT(2) | BIT(0); /* WRITE | MMD-access | EXEC */ - sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); - - err = rtmdio_839x_smi_wait_op(100000); - if (err) - goto errout; - - pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); - -errout: - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* - * The RTL930x family has 12 SerDes of three types. They are accessed through two IO registers at - * 0xbb0003b0 which simulate commands to an internal MDIO bus: - * - * - SerDes 0-1 exist on the RTL9301 and 9302B and are QSGMII capable - * - SerDes 2-9 are USXGMII capabable with either quad or single configuration - * - SerDes 10-11 are 10GBase-R capable - */ - -int rtmdio_930x_read_sds_phy(int sds, int page, int regnum) -{ - int i, ret = -EIO; - u32 cmd; - - if (sds < 0 || sds > 11 || page < 0 || page > 63 || regnum < 0 || regnum > 31) - return -EIO; - - mutex_lock(&rtmdio_lock_sds); - - cmd = sds << 2 | page << 7 | regnum << 13 | 1; - sw_w32(cmd, RTMDIO_930X_SDS_INDACS_CMD); - - for (i = 0; i < 100; i++) { - if (!(sw_r32(RTMDIO_930X_SDS_INDACS_CMD) & 0x1)) - break; - mdelay(1); - } - - if (i < 100) - ret = sw_r32(RTMDIO_930X_SDS_INDACS_DATA) & 0xffff; - - mutex_unlock(&rtmdio_lock_sds); - - return ret; -} - -int rtmdio_930x_write_sds_phy(int sds, int page, int regnum, u16 val) -{ - int i, ret = -EIO; - u32 cmd; - - if (sds < 0 || sds > 11 || page < 0 || page > 63 || regnum < 0 || regnum > 31) - return -EIO; - - mutex_lock(&rtmdio_lock_sds); - - cmd = sds << 2 | page << 7 | regnum << 13 | 0x3; - sw_w32(val, RTMDIO_930X_SDS_INDACS_DATA); - sw_w32(cmd, RTMDIO_930X_SDS_INDACS_CMD); - - for (i = 0; i < 100; i++) { - if (!(sw_r32(RTMDIO_930X_SDS_INDACS_CMD) & 0x1)) - break; - mdelay(1); - } - - mutex_unlock(&rtmdio_lock_sds); - - if (i < 100) - ret = 0; - - return ret; -} - -/* RTL930x specific MDIO functions */ - -static int rtmdio_930x_write_phy(u32 port, u32 page, u32 reg, u32 val) -{ - u32 v; - int err = 0; - - pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, val); - - if (port > 63 || page > 4095 || reg > 31) - return -ENOTSUPP; - - val &= 0xffff; - mutex_lock(&rtmdio_lock); - - sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); - sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); - v = reg << 20 | page << 3 | 0x1f << 15 | BIT(2) | BIT(0); - sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); - - do { - v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while (v & 0x1); - - if (v & 0x2) - err = -EIO; - - mutex_unlock(&rtmdio_lock); - - return err; -} - -static int rtmdio_930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) -{ - u32 v; - int err = 0; - - if (port > 63 || page > 4095 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); - v = reg << 20 | page << 3 | 0x1f << 15 | 1; - sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); - - do { - v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while ( v & 0x1); - - if (v & BIT(25)) { - pr_debug("Error reading phy %d, register %d\n", port, reg); - err = -EIO; - } - *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); - - pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val); - - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* Write to an mmd register of the PHY */ -static int rtmdio_930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) -{ - int err = 0; - u32 v; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access */ - sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); - - /* Set data to write */ - sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); - - v = BIT(2) | BIT(1) | BIT(0); /* WRITE | MMD-access | EXEC */ - sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); - - do { - v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while (v & BIT(0)); - - pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); - mutex_unlock(&rtmdio_lock); - return err; -} - -/* Read an mmd register of the PHY */ -static int rtmdio_930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) -{ - int err = 0; - u32 v; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access */ - sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); - - v = BIT(1) | BIT(0); /* MMD-access | EXEC */ - sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); - - do { - v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while (v & BIT(0)); - /* There is no error-checking via BIT 25 of v, as it does not seem to be set correctly */ - *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); - pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); - - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* - * The RTL931x family has 14 "frontend" SerDes that are cascaded. All operations (e.g. reset) work - * on this frontend view while their registers are distributed over a total of least 26 background - * SerDes with 64 pages and 32 registers. Three types of SerDes exist: - * - * - Serdes 0,1 are "simple" and work on one background serdes. - * - "Even" SerDes with numbers 2, 4, 6, 8, 10, 12 work on two background SerDes. One analog and - * one digital. - * - "Odd" SerDes with numbers 3, 5, 7, 9, 11, 13 work on a total of 3 background SerDes (one analog - * and two digital) - * - * This maps to: - * - * Frontend SerDes | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - * -----------------+------------------------------------------ - * Backend SerDes 1 | 0 1 2 3 6 7 10 11 14 15 18 19 22 23 - * Backend SerDes 2 | 0 1 2 4 6 8 10 12 14 16 18 20 22 24 - * Backend SerDes 3 | 0 1 3 5 7 9 11 13 15 17 19 21 23 25 - * - * Note: In Realtek proprietary XSGMII mode (10G pumped SGMII) the frontend SerDes works on the - * two digital SerDes while in all other modes it works on the analog and the first digital SerDes. - * Overlapping (e.g. backend SerDes 7 can be analog or digital 2) is avoided by the existing - * hardware designs. - * - * Align this for readability by simulating a total of 576 pages and mix them as follows. - * - * frontend page "even" frontend SerDes "odd" frontend SerDes - * page 0x000-0x03f (analog): page 0x000-0x03f back SDS page 0x000-0x03f back SDS - * page 0x100-0x13f (digi 1): page 0x000-0x03f back SDS page 0x000-0x03f back SDS+1 - * page 0x200-0x23f (digi 2): page 0x000-0x03f back SDS+1 page 0x000-0x03f back SDS+2 - */ - -static int rtmdio_931x_get_backing_sds(u32 sds, u32 page) -{ - int map[] = {0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23}; - int back = map[sds]; - - if (page & 0xc0) - return -EINVAL; /* hole */ - - if (sds >= 2) { - if (sds & 1) - back += (page >> 8); /* distribute "odd" to 3 background SerDes */ - else - back += (page >> 9); /* distribute "even" to 2 background SerDes */ - } - - return back; -} - -static int rtmdio_931x_read_sds_phy(int sds, int page, int regnum) -{ - u32 cmd = sds << 2 | page << 7 | regnum << 13 | 1; - int i, ret = -EIO; - - pr_debug("%s: phy_addr(SDS-ID) %d, phy_reg: %d\n", __func__, sds, regnum); - - mutex_lock(&rtmdio_lock_sds); - sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); - - for (i = 0; i < 100; i++) { - if (!(sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) - break; - mdelay(1); - } - - if (i < 100) - ret = sw_r32(RTMDIO_931X_SERDES_INDRT_DATA_CTRL) & 0xffff; - - mutex_unlock(&rtmdio_lock_sds); - - pr_debug("%s: returning %08x\n", __func__, ret); - - return ret; -} - -int rtmdio_931x_read_sds_phy_new(int sds, int page, int regnum) -{ - int backsds = rtmdio_931x_get_backing_sds(sds, page); - - return backsds < 0 ? 0 : rtmdio_931x_read_sds_phy(backsds, page & 0x3f, regnum); -} - -static int rtmdio_931x_write_sds_phy(int sds, int page, int regnum, u16 val) -{ - u32 cmd = sds << 2 | page << 7 | regnum << 13;; - int i, ret = -EIO; - - mutex_lock(&rtmdio_lock_sds); - sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); - sw_w32(val, RTMDIO_931X_SERDES_INDRT_DATA_CTRL); - - cmd = sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) | 0x3; - sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); - - for (i = 0; i < 100; i++) { - if (!(sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) - break; - mdelay(1); - } - - mutex_unlock(&rtmdio_lock_sds); - - if (i < 100) - ret = 0; - - return ret; -} - -int rtmdio_931x_write_sds_phy_new(int sds, int page, int regnum, u16 val) -{ - int backsds = rtmdio_931x_get_backing_sds(sds, page); - - return backsds < 0 ? 0 : rtmdio_931x_write_sds_phy(backsds, page & 0x3f, regnum, val); -} - -/* RTL931x specific MDIO functions */ - -static int rtmdio_931x_write_phy(u32 port, u32 page, u32 reg, u32 val) -{ - u32 v; - int err = 0; - - val &= 0xffff; - if (port > 63 || page > 4095 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - pr_debug("%s: writing to phy %d %d %d %d\n", __func__, port, page, reg, val); - /* Clear both port registers */ - sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2); - sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4); - sw_w32_mask(0, BIT(port % 32), RTL931X_SMI_INDRT_ACCESS_CTRL_2 + (port / 32) * 4); - - sw_w32_mask(0xffff, val, RTL931X_SMI_INDRT_ACCESS_CTRL_3); - - v = reg << 6 | page << 11 ; - sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); - - sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1); - - v |= BIT(4) | 1; /* Write operation and execute */ - sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); - - do { - } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); - - if (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x2) - err = -EIO; - - mutex_unlock(&rtmdio_lock); - - return err; -} - -static int rtmdio_931x_read_phy(u32 port, u32 page, u32 reg, u32 *val) -{ - u32 v; - - if (port > 63 || page > 4095 || reg > 31) - return -ENOTSUPP; - - mutex_lock(&rtmdio_lock); - - sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); - - v = reg << 6 | page << 11 | 1; - sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); - - do { - } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); - - v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); - *val = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3); - *val = (*val & 0xffff0000) >> 16; - - pr_debug("%s: port %d, page: %d, reg: %x, val: %x, v: %08x\n", - __func__, port, page, reg, *val, v); - - mutex_unlock(&rtmdio_lock); - - return 0; -} - -/* Read an mmd register of the PHY */ -static int rtmdio_931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) -{ - int err = 0; - u32 v; - /* Select PHY register type - * If select 1G/10G MMD register type, registers EXT_PAGE, MAIN_PAGE and REG settings are don’t care. - * 0x0 Normal register (Clause 22) - * 0x1: 1G MMD register (MMD via Clause 22 registers 13 and 14) - * 0x2: 10G MMD register (MMD via Clause 45) - */ - int type = 2; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access via port-number */ - sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | regnum, RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); - - v = type << 2 | BIT(0); /* MMD-access-type | EXEC */ - sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); - - do { - v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); - } while (v & BIT(0)); - - /* Check for error condition */ - if (v & BIT(1)) - err = -EIO; - - *val = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) >> 16; - - pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, - port, devnum, regnum, *val, err); - - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* Write to an mmd register of the PHY */ -static int rtmdio_931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) -{ - int err = 0; - u32 v; - int type = 2; - u64 pm; - - mutex_lock(&rtmdio_lock); - - /* Set PHY to access via port-mask */ - pm = (u64)1 << port; - sw_w32((u32)pm, RTL931X_SMI_INDRT_ACCESS_CTRL_2); - sw_w32((u32)(pm >> 32), RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4); - - /* Set data to write */ - sw_w32_mask(0xffff, val, RTL931X_SMI_INDRT_ACCESS_CTRL_3); - - /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | regnum, RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); - - v = BIT(4) | type << 2 | BIT(0); /* WRITE | MMD-access-type | EXEC */ - sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); - - do { - v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); - } while (v & BIT(0)); - - pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, - port, devnum, regnum, val, err); - mutex_unlock(&rtmdio_lock); - - return err; -} - -/* These are the core functions of our new Realtek SoC MDIO bus. */ - -static int rtmdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err, val; - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - err = (*priv->read_mmd_phy)(addr, devnum, regnum, &val); - pr_debug("rd_MMD(adr=%d, dev=%d, reg=%d) = %d, err = %d\n", - addr, devnum, regnum, val, err); - return err ? err : val; -} - -static int rtmdio_map_sds_register(int page, int regnum, int *sds_page, int *sds_regnum) -{ - /* - * For the SerDes PHY simulate a register mapping like common RealTek PHYs do. Always - * keep the common registers 0x00-0x0f in place and map the SerDes registers into the - * upper vendor specific registers 0x10-0x17 according to the page select register - * (0x1f). That gives a register mapping as follows: - * - * +-----------------------+-----------------------+---------------+-----------------+ - * | reg 0x00-0x0f | reg 0x10-0x17 | reg 0x18-0x1e | reg 0x1f | - * +-----------------------+-----------------------+---------------+-----------------+ - * | SerDes fiber page (2) | real SerDes registers | zero | SerDes page | - * | registers 0x00-0x0f | in packages of 8 | | select register | - * +-----------------------+-----------------------+---------------+-----------------+ - */ - - if (regnum < 16) { - *sds_page = 2; - *sds_regnum = regnum; - } else if (regnum < 24) { - *sds_page = page / 4; - *sds_regnum = 8 * (page % 4) + (regnum - 16); - } else - return 0; - - return 1; -} - -static int rtmdio_read_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum) -{ - int ret, sds_page, sds_regnum; - - ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); - if (ret) - ret = priv->read_sds_phy(sds, sds_page, sds_regnum); - pr_debug("rd_SDS(sds=%d, pag=%d, reg=%d) = %d\n", sds, page, regnum, ret); - - return ret; -} - -static int rtmdio_write_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum, u16 val) -{ - int ret, sds_page, sds_regnum; - - ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); - if (ret) - ret = priv->write_sds_phy(sds, sds_page, sds_regnum, val); - pr_debug("wr_SDS(sds=%d, pag=%d, reg=%d, val=%d) err = %d\n", sds, page, regnum, val, ret); - - return ret; -} - -static int rtmdio_read(struct mii_bus *bus, int addr, int regnum) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err, val; - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - if (regnum == RTMDIO_PAGE_SELECT && priv->page[addr] != priv->rawpage) - return priv->page[addr]; - - priv->raw[addr] = (priv->page[addr] == priv->rawpage); - if ((priv->phy_is_internal[addr]) && (priv->sds_id[addr] >= 0)) - return rtmdio_read_sds_phy(priv, priv->sds_id[addr], - priv->page[addr], regnum); - - err = (*priv->read_phy)(addr, priv->page[addr], regnum, &val); - pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", - addr, priv->page[addr], regnum, val, err); - return err ? err : val; -} - -static int rtmdio_93xx_read(struct mii_bus *bus, int addr, int regnum) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err, val; - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - if (regnum == RTMDIO_PAGE_SELECT && priv->page[addr] != priv->rawpage) - return priv->page[addr]; - - priv->raw[addr] = (priv->page[addr] == priv->rawpage); - if (priv->phy_is_internal[addr]) { - return rtmdio_931x_read_sds_phy(priv->sds_id[addr], - priv->page[addr], regnum); - } - - err = (*priv->read_phy)(addr, priv->page[addr], regnum, &val); - pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", - addr, priv->page[addr], regnum, val, err); - return err ? err : val; -} - -static int rtmdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err; - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - err = (*priv->write_mmd_phy)(addr, devnum, regnum, val); - pr_debug("wr_MMD(adr=%d, dev=%d, reg=%d, val=%d) err = %d\n", - addr, devnum, regnum, val, err); - return err; -} - -static int rtmdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err, page; - - if (regnum == RTMDIO_PORT_SELECT) { - priv->extaddr = (s16)val; - return 0; - } - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - page = priv->page[addr]; - - if (regnum == RTMDIO_PAGE_SELECT) - priv->page[addr] = val; - - if (!priv->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == priv->rawpage)) { - priv->raw[addr] = (page == priv->rawpage); - if (priv->phy_is_internal[addr] && priv->sds_id[addr] >= 0) - return rtmdio_write_sds_phy(priv, priv->sds_id[addr], - priv->page[addr], regnum, val); - - err = (*priv->write_phy)(addr, page, regnum, val); - pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", - addr, page, regnum, val, err); - return err; - } - - priv->raw[addr] = false; - return 0; -} - -static int rtmdio_93xx_write(struct mii_bus *bus, int addr, int regnum, u16 val) -{ - struct rtmdio_bus_priv *priv = bus->priv; - int err, page; - - if (regnum == RTMDIO_PORT_SELECT) { - priv->extaddr = (s16)val; - return 0; - } - - if (priv->extaddr >= 0) - addr = priv->extaddr; - - if (addr >= priv->cpu_port) - return -ENODEV; - - page = priv->page[addr]; - - if (regnum == RTMDIO_PAGE_SELECT) - priv->page[addr] = val; - - if (!priv->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == priv->rawpage)) { - priv->raw[addr] = (page == priv->rawpage); - if (priv->phy_is_internal[addr]) { - return rtmdio_931x_write_sds_phy(priv->sds_id[addr], - page, regnum, val); - } - - err = (*priv->write_phy)(addr, page, regnum, val); - pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", - addr, page, regnum, val, err); - } - - priv->raw[addr] = false; - return 0; -} - -static int rtmdio_838x_reset(struct mii_bus *bus) -{ - pr_debug("%s called\n", __func__); - /* Disable MAC polling the PHY so that we can start configuration */ - sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); - - /* Enable PHY control via SoC */ - sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); - - /* Probably should reset all PHYs here... */ - return 0; -} - -static int rtmdio_839x_reset(struct mii_bus *bus) -{ - return 0; - - pr_debug("%s called\n", __func__); - /* BUG: The following does not work, but should! */ - /* Disable MAC polling the PHY so that we can start configuration */ - sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL); - sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL + 4); - /* Disable PHY polling via SoC */ - sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); - - /* Probably should reset all PHYs here... */ - return 0; -} - -u8 mac_type_bit[RTL930X_CPU_PORT] = {0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, - 8, 8, 8, 8, 10, 10, 10, 10, 12, 15, 18, 21}; - -static int rtmdio_930x_reset(struct mii_bus *bus) -{ - struct rtmdio_bus_priv *priv = bus->priv; - bool uses_usxgmii = false; /* For the Aquantia PHYs */ - bool uses_hisgmii = false; /* For the RTL8221/8226 */ - u32 private_poll_mask = 0; - u32 poll_sel[2] = { 0 }; - u32 poll_ctrl = 0; - u32 c45_mask = 0; - u32 v; - - /* Mapping of port to phy-addresses on an SMI bus */ - for (int i = 0; i < RTL930X_CPU_PORT; i++) { - int pos; - - if (priv->smi_bus[i] < 0) - continue; - - pos = (i % 6) * 5; - sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, - RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4); - - pos = (i * 2) % 32; - poll_sel[i / 16] |= priv->smi_bus[i] << pos; - poll_ctrl |= BIT(20 + priv->smi_bus[i]); - } - - /* Configure which SMI bus is behind which port number */ - sw_w32(poll_sel[0], RTL930X_SMI_PORT0_15_POLLING_SEL); - sw_w32(poll_sel[1], RTL930X_SMI_PORT16_27_POLLING_SEL); - - /* Disable POLL_SEL for any SMI bus with a normal PHY (not RTL8295R for SFP+) */ - sw_w32_mask(poll_ctrl, 0, RTL930X_SMI_GLB_CTRL); - - /* Configure which SMI busses are polled in c45 based on a c45 PHY being on that bus */ - for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) - if (priv->smi_bus_isc45[i]) - c45_mask |= BIT(i + 16); - - pr_info("c45_mask: %08x\n", c45_mask); - sw_w32_mask(GENMASK(19, 16), c45_mask, RTL930X_SMI_GLB_CTRL); - - /* Set the MAC type of each port according to the PHY-interface */ - /* Values are FE: 2, GE: 3, XGE/2.5G: 0(SERDES) or 1(otherwise), SXGE: 0 */ - v = 0; - for (int i = 0; i < RTL930X_CPU_PORT; i++) { - switch (priv->interfaces[i]) { - case PHY_INTERFACE_MODE_10GBASER: - break; /* Serdes: Value = 0 */ - case PHY_INTERFACE_MODE_HSGMII: - private_poll_mask |= BIT(i); - fallthrough; - case PHY_INTERFACE_MODE_USXGMII: - v |= BIT(mac_type_bit[i]); - uses_usxgmii = true; - break; - case PHY_INTERFACE_MODE_QSGMII: - private_poll_mask |= BIT(i); - v |= 3 << mac_type_bit[i]; - break; - default: - break; - } - } - sw_w32(v, RTL930X_SMI_MAC_TYPE_CTRL); - - /* Set the private polling mask for all Realtek PHYs (i.e. not the 10GBit Aquantia ones) */ - sw_w32(private_poll_mask, RTL930X_SMI_PRVTE_POLLING_CTRL); - - /* The following magic values are found in the port configuration, they seem to - * define different ways of polling a PHY. The below is for the Aquantia PHYs of - * the XGS1250 and the RTL8226 of the XGS1210 - */ - if (uses_usxgmii) { - sw_w32(0x01010000, RTL930X_SMI_10GPHY_POLLING_REG0_CFG); - sw_w32(0x01E7C400, RTL930X_SMI_10GPHY_POLLING_REG9_CFG); - sw_w32(0x01E7E820, RTL930X_SMI_10GPHY_POLLING_REG10_CFG); - } - if (uses_hisgmii) { - sw_w32(0x011FA400, RTL930X_SMI_10GPHY_POLLING_REG0_CFG); - sw_w32(0x013FA412, RTL930X_SMI_10GPHY_POLLING_REG9_CFG); - sw_w32(0x017FA414, RTL930X_SMI_10GPHY_POLLING_REG10_CFG); - } - - pr_debug("%s: RTL930X_SMI_GLB_CTRL %08x\n", __func__, - sw_r32(RTL930X_SMI_GLB_CTRL)); - pr_debug("%s: RTL930X_SMI_PORT0_15_POLLING_SEL %08x\n", __func__, - sw_r32(RTL930X_SMI_PORT0_15_POLLING_SEL)); - pr_debug("%s: RTL930X_SMI_PORT16_27_POLLING_SEL %08x\n", __func__, - sw_r32(RTL930X_SMI_PORT16_27_POLLING_SEL)); - pr_debug("%s: RTL930X_SMI_MAC_TYPE_CTRL %08x\n", __func__, - sw_r32(RTL930X_SMI_MAC_TYPE_CTRL)); - pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG0_CFG %08x\n", __func__, - sw_r32(RTL930X_SMI_10GPHY_POLLING_REG0_CFG)); - pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG9_CFG %08x\n", __func__, - sw_r32(RTL930X_SMI_10GPHY_POLLING_REG9_CFG)); - pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG10_CFG %08x\n", __func__, - sw_r32(RTL930X_SMI_10GPHY_POLLING_REG10_CFG)); - pr_debug("%s: RTL930X_SMI_PRVTE_POLLING_CTRL %08x\n", __func__, - sw_r32(RTL930X_SMI_PRVTE_POLLING_CTRL)); - - return 0; -} - -static int rtmdio_931x_reset(struct mii_bus *bus) -{ - struct rtmdio_bus_priv *priv = bus->priv; - u32 poll_sel[4] = { 0 }; - u32 poll_ctrl = 0; - u32 c45_mask = 0; - - pr_info("%s called\n", __func__); - /* Disable port polling for configuration purposes */ - sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL); - sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL + 4); - msleep(100); - - /* Mapping of port to phy-addresses on an SMI bus */ - for (int i = 0; i < RTL931X_CPU_PORT; i++) { - u32 pos; - - if (priv->smi_bus[i] < 0) - continue; - - pos = (i % 6) * 5; - sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, RTL931X_SMI_PORT_ADDR + (i / 6) * 4); - pos = (i * 2) % 32; - poll_sel[i / 16] |= priv->smi_bus[i] << pos; - poll_ctrl |= BIT(20 + priv->smi_bus[i]); - } - - /* Configure which SMI bus is behind which port number */ - for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) { - pr_info("poll sel %d, %08x\n", i, poll_sel[i]); - sw_w32(poll_sel[i], RTL931X_SMI_PORT_POLLING_SEL + (i * 4)); - } - - /* Configure which SMI busses */ - pr_info("c45_mask: %08x, RTL931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTL931X_SMI_GLB_CTRL0)); - for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) { - /* bus is polled in c45 */ - if (priv->smi_bus_isc45[i]) - c45_mask |= 0x2 << (i * 2); /* Std. C45, non-standard is 0x3 */ - } - - pr_info("c45_mask: %08x, RTL931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTL931X_SMI_GLB_CTRL0)); - - /* We have a 10G PHY enable polling - * sw_w32(0x01010000, RTL931X_SMI_10GPHY_POLLING_SEL2); - * sw_w32(0x01E7C400, RTL931X_SMI_10GPHY_POLLING_SEL3); - * sw_w32(0x01E7E820, RTL931X_SMI_10GPHY_POLLING_SEL4); - */ - sw_w32_mask(GENMASK(7, 0), c45_mask, RTL931X_SMI_GLB_CTRL1); - - return 0; -} - -static int rtl931x_chip_init(struct rtl838x_eth_priv *priv) -{ - pr_info("In %s\n", __func__); - - /* Initialize Encapsulation memory and wait until finished */ - sw_w32(0x1, RTL931X_MEM_ENCAP_INIT); - do { } while (sw_r32(RTL931X_MEM_ENCAP_INIT) & 1); - pr_info("%s: init ENCAP done\n", __func__); - - /* Initialize Managemen Information Base memory and wait until finished */ - sw_w32(0x1, RTL931X_MEM_MIB_INIT); - do { } while (sw_r32(RTL931X_MEM_MIB_INIT) & 1); - pr_info("%s: init MIB done\n", __func__); - - /* Initialize ACL (PIE) memory and wait until finished */ - sw_w32(0x1, RTL931X_MEM_ACL_INIT); - do { } while (sw_r32(RTL931X_MEM_ACL_INIT) & 1); - pr_info("%s: init ACL done\n", __func__); - - /* Initialize ALE memory and wait until finished */ - sw_w32(0xFFFFFFFF, RTL931X_MEM_ALE_INIT_0); - do { } while (sw_r32(RTL931X_MEM_ALE_INIT_0)); - sw_w32(0x7F, RTL931X_MEM_ALE_INIT_1); - sw_w32(0x7ff, RTL931X_MEM_ALE_INIT_2); - do { } while (sw_r32(RTL931X_MEM_ALE_INIT_2) & 0x7ff); - pr_info("%s: init ALE done\n", __func__); - - /* Enable ESD auto recovery */ - sw_w32(0x1, RTL931X_MDX_CTRL_RSVD); - - /* Init SPI, is this for thermal control or what? */ - sw_w32_mask(0x7 << 11, 0x2 << 11, RTL931X_SPI_CTRL0); - - return 0; -} - -static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) -{ - struct rtmdio_bus_priv *bus_priv; - struct device_node *mii_np, *dn; - int i, ret; - u32 pn; - - pr_debug("%s called\n", __func__); - mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); - - if (!mii_np) { - dev_err(&priv->pdev->dev, "no %s child node found", "mdio-bus"); - return -ENODEV; - } - - if (!of_device_is_available(mii_np)) { - ret = -ENODEV; - goto err_put_node; - } - - priv->mii_bus = devm_mdiobus_alloc_size(&priv->pdev->dev, sizeof(*bus_priv)); - if (!priv->mii_bus) { - ret = -ENOMEM; - goto err_put_node; - } - - bus_priv = priv->mii_bus->priv; - bus_priv->id = soc_info.id; - bus_priv->family_id = soc_info.family; - for (i=0; i < RTMDIO_MAX_PORT; i++) { - bus_priv->page[i] = 0; - bus_priv->raw[i] = false; - } - bus_priv->extaddr = -1; - - switch(priv->family_id) { - case RTL8380_FAMILY_ID: - priv->mii_bus->name = "rtl838x-eth-mdio"; - priv->mii_bus->read = rtmdio_read; - priv->mii_bus->write = rtmdio_write; - priv->mii_bus->reset = rtmdio_838x_reset; - bus_priv->read_sds_phy = rtmdio_838x_read_sds_phy; - bus_priv->write_sds_phy = rtmdio_838x_write_sds_phy; - bus_priv->read_mmd_phy = rtmdio_838x_read_mmd_phy; - bus_priv->write_mmd_phy = rtmdio_838x_write_mmd_phy; - bus_priv->read_phy = rtmdio_838x_read_phy; - bus_priv->write_phy = rtmdio_838x_write_phy; - bus_priv->cpu_port = RTL838X_CPU_PORT; - bus_priv->rawpage = 0xfff; - break; - case RTL8390_FAMILY_ID: - priv->mii_bus->name = "rtl839x-eth-mdio"; - priv->mii_bus->read = rtmdio_read; - priv->mii_bus->write = rtmdio_write; - priv->mii_bus->reset = rtmdio_839x_reset; - bus_priv->read_sds_phy = rtmdio_839x_read_sds_phy; - bus_priv->write_sds_phy = rtmdio_839x_write_sds_phy; - bus_priv->read_mmd_phy = rtmdio_839x_read_mmd_phy; - bus_priv->write_mmd_phy = rtmdio_839x_write_mmd_phy; - bus_priv->read_phy = rtmdio_839x_read_phy; - bus_priv->write_phy = rtmdio_839x_write_phy; - bus_priv->cpu_port = RTL839X_CPU_PORT; - bus_priv->rawpage = 0x1fff; - break; - case RTL9300_FAMILY_ID: - priv->mii_bus->name = "rtl930x-eth-mdio"; - priv->mii_bus->read = rtmdio_read; - priv->mii_bus->write = rtmdio_write; - priv->mii_bus->reset = rtmdio_930x_reset; - bus_priv->read_sds_phy = rtmdio_930x_read_sds_phy; - bus_priv->write_sds_phy = rtmdio_930x_write_sds_phy; - bus_priv->read_mmd_phy = rtmdio_930x_read_mmd_phy; - bus_priv->write_mmd_phy = rtmdio_930x_write_mmd_phy; - bus_priv->read_phy = rtmdio_930x_read_phy; - bus_priv->write_phy = rtmdio_930x_write_phy; - bus_priv->cpu_port = RTL930X_CPU_PORT; - bus_priv->rawpage = 0xfff; - break; - case RTL9310_FAMILY_ID: - priv->mii_bus->name = "rtl931x-eth-mdio"; - priv->mii_bus->read = rtmdio_93xx_read; - priv->mii_bus->write = rtmdio_93xx_write; - priv->mii_bus->reset = rtmdio_931x_reset; - bus_priv->read_mmd_phy = rtmdio_931x_read_mmd_phy; - bus_priv->write_mmd_phy = rtmdio_931x_write_mmd_phy; - bus_priv->read_phy = rtmdio_931x_read_phy; - bus_priv->write_phy = rtmdio_931x_write_phy; - bus_priv->cpu_port = RTL931X_CPU_PORT; - bus_priv->rawpage = 0x1fff; - break; - } - priv->mii_bus->read_c45 = rtmdio_read_c45; - priv->mii_bus->write_c45 = rtmdio_write_c45; - priv->mii_bus->parent = &priv->pdev->dev; - priv->mii_bus->phy_mask = ~(BIT_ULL(bus_priv->cpu_port) - 1ULL); - - for_each_node_by_name(dn, "ethernet-phy") { - u32 smi_addr[2]; - - if (of_property_read_u32(dn, "reg", &pn)) - continue; - - if (pn >= RTMDIO_MAX_PORT) { - pr_err("%s: illegal port number %d\n", __func__, pn); - return -ENODEV; - } - - if (of_property_read_u32(dn, "sds", &bus_priv->sds_id[pn])) - bus_priv->sds_id[pn] = -1; - else - pr_info("set sds port %d to %d\n", pn, bus_priv->sds_id[pn]); - - if (of_property_read_u32_array(dn, "rtl9300,smi-address", &smi_addr[0], 2)) { - bus_priv->smi_bus[pn] = 0; - bus_priv->smi_addr[pn] = pn; - } else { - bus_priv->smi_bus[pn] = smi_addr[0]; - bus_priv->smi_addr[pn] = smi_addr[1]; - } - - if (bus_priv->smi_bus[pn] >= RTMDIO_MAX_SMI_BUS) { - pr_err("%s: illegal SMI bus number %d\n", __func__, bus_priv->smi_bus[pn]); - return -ENODEV; - } - - bus_priv->phy_is_internal[pn] = of_property_read_bool(dn, "phy-is-integrated"); - - if (bus_priv->phy_is_internal[pn] && bus_priv->sds_id[pn] >= 0) - bus_priv->smi_bus[pn]= -1; - else if (of_device_is_compatible(dn, "ethernet-phy-ieee802.3-c45")) - bus_priv->smi_bus_isc45[bus_priv->smi_bus[pn]] = true; - } - - dn = of_find_compatible_node(NULL, NULL, "realtek,rtl83xx-switch"); - if (!dn) { - dev_err(&priv->pdev->dev, "No RTL switch node in DTS\n"); - return -ENODEV; - } - - for_each_node_by_name(dn, "port") { - if (of_property_read_u32(dn, "reg", &pn)) - continue; - pr_debug("%s Looking at port %d\n", __func__, pn); - if (pn > priv->cpu_port) - continue; - if (of_get_phy_mode(dn, &bus_priv->interfaces[pn])) - bus_priv->interfaces[pn] = PHY_INTERFACE_MODE_NA; - pr_debug("%s phy mode of port %d is %s\n", - __func__, pn, phy_modes(bus_priv->interfaces[pn])); - } - - snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np); - ret = devm_of_mdiobus_register(&priv->pdev->dev, priv->mii_bus, mii_np); - -err_put_node: - of_node_put(mii_np); - - return ret; -} - -static netdev_features_t rtl838x_fix_features(struct net_device *dev, - netdev_features_t features) -{ - return features; -} - -static int rtl83xx_set_features(struct net_device *dev, netdev_features_t features) -{ - struct rtl838x_eth_priv *priv = netdev_priv(dev); - - if ((features ^ dev->features) & NETIF_F_RXCSUM) { - if (!(features & NETIF_F_RXCSUM)) - sw_w32_mask(BIT(3), 0, priv->r->mac_port_ctrl(priv->cpu_port)); - else - sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); - } - - return 0; -} - -static int rtl93xx_set_features(struct net_device *dev, netdev_features_t features) -{ - struct rtl838x_eth_priv *priv = netdev_priv(dev); - - if ((features ^ dev->features) & NETIF_F_RXCSUM) { - if (!(features & NETIF_F_RXCSUM)) - sw_w32_mask(BIT(4), 0, priv->r->mac_port_ctrl(priv->cpu_port)); - else - sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); - } - - return 0; -} - -static struct phylink_pcs *rtl838x_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) -{ - struct net_device *dev = to_net_dev(config->dev); - struct rtl838x_eth_priv *priv = netdev_priv(dev); - - return &priv->pcs; -} - -static const struct net_device_ops rtl838x_eth_netdev_ops = { - .ndo_open = rtl838x_eth_open, - .ndo_stop = rtl838x_eth_stop, - .ndo_start_xmit = rtl838x_eth_tx, - .ndo_select_queue = rtl83xx_pick_tx_queue, - .ndo_set_mac_address = rtl838x_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, - .ndo_tx_timeout = rtl838x_eth_tx_timeout, - .ndo_set_features = rtl83xx_set_features, - .ndo_fix_features = rtl838x_fix_features, - .ndo_setup_tc = rtl83xx_setup_tc, -}; - -static const struct net_device_ops rtl839x_eth_netdev_ops = { - .ndo_open = rtl838x_eth_open, - .ndo_stop = rtl838x_eth_stop, - .ndo_start_xmit = rtl838x_eth_tx, - .ndo_select_queue = rtl83xx_pick_tx_queue, - .ndo_set_mac_address = rtl838x_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = rtl839x_eth_set_multicast_list, - .ndo_tx_timeout = rtl838x_eth_tx_timeout, - .ndo_set_features = rtl83xx_set_features, - .ndo_fix_features = rtl838x_fix_features, - .ndo_setup_tc = rtl83xx_setup_tc, -}; - -static const struct net_device_ops rtl930x_eth_netdev_ops = { - .ndo_open = rtl838x_eth_open, - .ndo_stop = rtl838x_eth_stop, - .ndo_start_xmit = rtl838x_eth_tx, - .ndo_select_queue = rtl93xx_pick_tx_queue, - .ndo_set_mac_address = rtl838x_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = rtl930x_eth_set_multicast_list, - .ndo_tx_timeout = rtl838x_eth_tx_timeout, - .ndo_set_features = rtl93xx_set_features, - .ndo_fix_features = rtl838x_fix_features, - .ndo_setup_tc = rtl83xx_setup_tc, -}; - -static const struct net_device_ops rtl931x_eth_netdev_ops = { - .ndo_open = rtl838x_eth_open, - .ndo_stop = rtl838x_eth_stop, - .ndo_start_xmit = rtl838x_eth_tx, - .ndo_select_queue = rtl93xx_pick_tx_queue, - .ndo_set_mac_address = rtl838x_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = rtl931x_eth_set_multicast_list, - .ndo_tx_timeout = rtl838x_eth_tx_timeout, - .ndo_set_features = rtl93xx_set_features, - .ndo_fix_features = rtl838x_fix_features, -}; - -static const struct phylink_pcs_ops rtl838x_pcs_ops = { - .pcs_get_state = rtl838x_pcs_get_state, - .pcs_an_restart = rtl838x_pcs_an_restart, - .pcs_config = rtl838x_pcs_config, -}; - -static const struct phylink_mac_ops rtl838x_phylink_ops = { - .mac_select_pcs = rtl838x_mac_select_pcs, - .mac_config = rtl838x_mac_config, - .mac_link_down = rtl838x_mac_link_down, - .mac_link_up = rtl838x_mac_link_up, -}; - -static const struct ethtool_ops rtl838x_ethtool_ops = { - .get_link_ksettings = rtl838x_get_link_ksettings, - .set_link_ksettings = rtl838x_set_link_ksettings, -}; - -static int __init rtl838x_eth_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct device_node *dn = pdev->dev.of_node; - struct rtl838x_eth_priv *priv; - struct resource *res, *mem; - phy_interface_t phy_mode; - struct phylink *phylink; - u8 mac_addr[ETH_ALEN]; - int err = 0, rxrings, rxringlen; - struct ring_b *ring; - - pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n", - (u32)pdev, (u32)(&(pdev->dev))); - - if (!dn) { - dev_err(&pdev->dev, "No DT found\n"); - return -EINVAL; - } - - rxrings = (soc_info.family == RTL8380_FAMILY_ID - || soc_info.family == RTL8390_FAMILY_ID) ? 8 : 32; - rxrings = rxrings > MAX_RXRINGS ? MAX_RXRINGS : rxrings; - rxringlen = MAX_ENTRIES / rxrings; - rxringlen = rxringlen > MAX_RXLEN ? MAX_RXLEN : rxringlen; - - dev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(struct rtl838x_eth_priv), TXRINGS, rxrings); - if (!dev) - return -ENOMEM; - SET_NETDEV_DEV(dev, &pdev->dev); - priv = netdev_priv(dev); - - /* obtain buffer memory space */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) { - mem = devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), res->name); - if (!mem) { - dev_err(&pdev->dev, "cannot request memory space\n"); - return -ENXIO; - } - - dev->mem_start = mem->start; - dev->mem_end = mem->end; - } else { - dev_err(&pdev->dev, "cannot request IO resource\n"); - return -ENXIO; - } - - /* Allocate buffer memory */ - priv->membase = dmam_alloc_coherent(&pdev->dev, rxrings * rxringlen * RING_BUFFER + - sizeof(struct ring_b) + sizeof(struct notify_b), - (void *)&dev->mem_start, GFP_KERNEL); - if (!priv->membase) { - dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); - return -ENOMEM; - } - - /* Allocate ring-buffer space at the end of the allocated memory */ - ring = priv->membase; - ring->rx_space = priv->membase + sizeof(struct ring_b) + sizeof(struct notify_b); - - spin_lock_init(&priv->lock); - - dev->ethtool_ops = &rtl838x_ethtool_ops; - dev->min_mtu = ETH_ZLEN; - dev->max_mtu = DEFAULT_MTU; - dev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM; - dev->hw_features = NETIF_F_RXCSUM; + dev->ethtool_ops = &rtl838x_ethtool_ops; + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = DEFAULT_MTU; + dev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM; + dev->hw_features = NETIF_F_RXCSUM; priv->id = soc_info.id; priv->family_id = soc_info.family; @@ -3455,10 +1809,6 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) priv->pdev = pdev; priv->netdev = dev; - err = rtl838x_mdio_init(priv); - if (err) - return err; - for (int i = 0; i < priv->rxrings; i++) { priv->rx_qs[i].id = i; priv->rx_qs[i].priv = priv; diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h index f1e4b176091..f639bc6d43a 100644 --- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h @@ -454,20 +454,4 @@ struct rtl838x_eth_reg { bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag); }; -int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum); -int phy_package_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val); -int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum); -int phy_package_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val); -int phy_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum); -int phy_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val); - -int rtmdio_838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); -int rtmdio_838x_write_phy(u32 port, u32 page, u32 reg, u32 val); - -int rtmdio_930x_read_sds_phy(int sds, int page, int regnum); -int rtmdio_930x_write_sds_phy(int sds, int page, int regnum, u16 val); - -int rtmdio_931x_read_sds_phy_new(int sds, int page, int regnum); -int rtmdio_931x_write_sds_phy_new(int sds, int page, int regnum, u16 val); - #endif /* _RTL838X_ETH_H */ diff --git a/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c b/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c new file mode 100644 index 00000000000..fcc1c53e55b --- /dev/null +++ b/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c @@ -0,0 +1,1641 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include + +#define RTMDIO_MAX_PORT 57 +#define RTMDIO_MAX_SMI_BUS 4 +#define RTMDIO_PAGE_SELECT 0x1f + +#define RTMDIO_838X_CPU_PORT 28 +#define RTMDIO_838X_FAMILY_ID 0x8380 + +#define RTMDIO_839X_CPU_PORT 52 +#define RTMDIO_839X_FAMILY_ID 0x8390 + +#define RTMDIO_930X_CPU_PORT 28 +#define RTMDIO_930X_FAMILY_ID 0x9300 + +#define RTMDIO_931X_CPU_PORT 56 +#define RTMDIO_931X_FAMILY_ID 0x9310 + +/* Register base */ +#define RTMDIO_SW_BASE ((volatile void *) 0xBB000000) + +/* MDIO bus registers */ +#define RTMDIO_838X_SMI_GLB_CTRL (0xa100) +#define RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0 (0xa1b8) +#define RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1 (0xa1bc) +#define RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2 (0xa1c0) +#define RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3 (0xa1c4) +#define RTMDIO_838X_SMI_POLL_CTRL (0xa17c) + +#define RTMDIO_839X_PHYREG_CTRL (0x03E0) +#define RTMDIO_839X_PHYREG_PORT_CTRL (0x03E4) +#define RTMDIO_839X_PHYREG_ACCESS_CTRL (0x03DC) +#define RTMDIO_839X_PHYREG_DATA_CTRL (0x03F0) +#define RTMDIO_839X_PHYREG_MMD_CTRL (0x03F4) +#define RTMDIO_839X_SMI_PORT_POLLING_CTRL (0x03fc) +#define RTMDIO_839X_SMI_GLB_CTRL (0x03f8) + +#define RTMDIO_930X_SMI_GLB_CTRL (0xCA00) +#define RTMDIO_930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70) +#define RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74) +#define RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78) +#define RTMDIO_930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C) +#define RTMDIO_930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTMDIO_930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) +#define RTMDIO_930X_SMI_MAC_TYPE_CTRL (0xCA04) +#define RTMDIO_930X_SMI_PRVTE_POLLING_CTRL (0xCA10) +#define RTMDIO_930X_SMI_10G_POLLING_REG0_CFG (0xCBB4) +#define RTMDIO_930X_SMI_10G_POLLING_REG9_CFG (0xCBB8) +#define RTMDIO_930X_SMI_10G_POLLING_REG10_CFG (0xCBBC) +#define RTMDIO_930X_SMI_PORT0_5_ADDR (0xCB80) + +#define RTMDIO_931X_SMI_PORT_POLLING_CTRL (0x0CCC) +#define RTMDIO_931X_SMI_INDRT_ACCESS_BC_CTRL (0x0C14) +#define RTMDIO_931X_SMI_GLB_CTRL0 (0x0CC0) +#define RTMDIO_931X_SMI_GLB_CTRL1 (0x0CBC) +#define RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00) +#define RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04) +#define RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08) +#define RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10) +#define RTMDIO_931X_SMI_INDRT_ACCESS_MMD_CTRL (0x0C18) +#define RTMDIO_931X_MAC_L2_GLOBAL_CTRL2 (0x1358) +#define RTMDIO_931X_SMI_PORT_POLLING_SEL (0x0C9C) +#define RTMDIO_931X_SMI_PORT_ADDR (0x0C74) + +/* MDIO SerDes registers */ +#define RTMDIO_838X_BASE (0xe780) + +#define RTMDIO_839X_BASE (0xa000) + +#define RTMDIO_930X_SDS_INDACS_CMD (0x03B0) +#define RTMDIO_930X_SDS_INDACS_DATA (0x03B4) + +#define RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL (0x5638) +#define RTMDIO_931X_SERDES_INDRT_DATA_CTRL (0x563C) + +/* Other registers */ +#define RTMDIO_839X_MODEL_NAME_INFO_REG (0x0ff0) +#define RTMDIO_93XX_MODEL_NAME_INFO_REG (0x0004) + +#define sw_r32(reg) readl(RTMDIO_SW_BASE + reg) +#define sw_w32(val, reg) writel(val, RTMDIO_SW_BASE + reg) +#define sw_w32_mask(clear, set, reg) sw_w32((sw_r32(reg) & ~(clear)) | (set), reg) + +int rtmdio_930x_read_sds_phy(int sds, int page, int regnum); +int rtmdio_930x_write_sds_phy(int sds, int page, int regnum, u16 val); + +int rtmdio_931x_read_sds_phy_new(int sds, int page, int regnum); +int rtmdio_931x_write_sds_phy_new(int sds, int page, int regnum, u16 val); + +/* + * On all Realtek switch platforms the hardware periodically reads the link status of all + * PHYs. This is to some degree programmable, so that one can tell the hardware to read + * specific C22 registers from specific pages, or C45 registers, to determine the current + * link speed, duplex, flow-control, ... + * + * This happens without any need for the driver to do anything at runtime, completely + * invisible and in a parallel hardware thread, independent of the CPU running Linux. + * All one needs to do is to set it up once. Having the MAC link settings automatically + * follow the PHY link status also happens to be the only way to control MAC port status + * in a meaningful way, or at least it's the only way we fully understand, as this is + * what every vendor firmware is doing. + * + * The hardware PHY polling unit doesn't care about bus locking, it just assumes that all + * paged PHY operations are also done via the same hardware unit offering this PHY access + * abstractions. + * + * Additionally at least the RTL838x and RTL839x devices are known to have a so called + * raw mode. Using the special MAX_PAGE-1 with the MDIO controller found in Realtek + * SoCs allows to access the PHY in raw mode, ie. bypassing the cache and paging engine + * of the MDIO controller. E.g. for RTL838x this is 0xfff. + * + * On the other hand Realtek PHYs usually make use of select register 0x1f to switch + * pages. There is no problem to issue separate page and access bus calls to the PHYs + * when they are not attached to an Realtek SoC. The paradigm should be to keep the PHY + * implementation bus independent. + * + * As if this is not enough the PHY packages consist of 4 or 8 ports that all can be + * programmed individually. Some registers are only available on port 0 and configure + * the whole package. + * + * To bring all this together we need a tricky bus design that intercepts select page + * calls but lets raw page accesses through. And especially knows how to handle raw + * accesses to the select register. Additionally we need the possibility to write to + * all 8 ports of the PHY individually. + * + * While the C45 clause stuff is pretty standard the legacy functions basically track + * the accesses and the state of the bus with the attributes page[], raw[] and portaddr + * of the bus_priv structure. The page selection works as follows: + * + * phy_write(phydev, RTMDIO_PAGE_SELECT, 12) : store internal page 12 in driver + * phy_write(phydev, 7, 33) : write page=12, reg=7, val=33 + * + * or simply + * + * phy_write_paged(phydev, 12, 7, 33) : write page=12, reg=7, val=33 + * + * Any Realtek PHY that will be connected to this bus must simply provide the standard + * page functions: + * + * define RTL821X_PAGE_SELECT 0x1f + * + * static int rtl821x_read_page(struct phy_device *phydev) + * { + * return __phy_read(phydev, RTL821X_PAGE_SELECT); + * } + * + * static int rtl821x_write_page(struct phy_device *phydev, int page) + * { + * return __phy_write(phydev, RTL821X_PAGE_SELECT, page); + * } + * + * In case there are non Realtek PHYs attached to the bus the logic might need to be + * reimplemented. For now it should be sufficient. + */ + +DEFINE_MUTEX(rtmdio_lock); +DEFINE_MUTEX(rtmdio_lock_sds); + +struct rtmdio_bus_priv { + u16 id; + u16 family_id; + int rawpage; + int cpu_port; + int page[RTMDIO_MAX_PORT]; + bool raw[RTMDIO_MAX_PORT]; + int smi_bus[RTMDIO_MAX_PORT]; + u8 smi_addr[RTMDIO_MAX_PORT]; + int sds_id[RTMDIO_MAX_PORT]; + bool smi_bus_isc45[RTMDIO_MAX_SMI_BUS]; + bool phy_is_internal[RTMDIO_MAX_PORT]; + phy_interface_t interfaces[RTMDIO_MAX_PORT]; + int (*read_mmd_phy)(u32 port, u32 addr, u32 reg, u32 *val); + int (*write_mmd_phy)(u32 port, u32 addr, u32 reg, u32 val); + int (*read_phy)(u32 port, u32 page, u32 reg, u32 *val); + int (*write_phy)(u32 port, u32 page, u32 reg, u32 val); + int (*read_sds_phy)(int sds, int page, int regnum); + int (*write_sds_phy)(int sds, int page, int regnum, u16 val); +}; + +/* SerDes reader/writer functions for the ports without external phy. */ + +/* + * The RTL838x has 6 SerDes. The 16 bit registers start at 0xbb00e780 and are mapped directly into + * 32 bit memory addresses. High 16 bits are always empty. A "lower" memory block serves pages 0/3 + * a "higher" memory block pages 1/2. + */ + +static int rtmdio_838x_reg_offset(int sds, int page, int regnum) +{ + if (sds < 0 || sds > 5) + return -EINVAL; + + if (page == 0 || page == 3) + return (sds << 9) + (page << 7) + (regnum << 2); + else if (page == 1 || page == 2) + return 0xb80 + (sds << 8) + (page << 7) + (regnum << 2); + + return -EINVAL; +} + +static int rtmdio_838x_read_sds_phy(int sds, int page, int regnum) +{ + int offset = rtmdio_838x_reg_offset(sds, page, regnum); + + if (offset < 0) + return offset; + + return sw_r32(RTMDIO_838X_BASE + offset) & GENMASK(15, 0); +} + +static int rtmdio_838x_write_sds_phy(int sds, int page, int regnum, u16 val) +{ + int offset = rtmdio_838x_reg_offset(sds, page, regnum); + + if (offset < 0) + return offset; + + sw_w32(val, RTMDIO_838X_BASE + offset); + + return 0; +} + +/* RTL838x specific MDIO functions */ + +static int rtmdio_838x_smi_wait_op(int timeout) +{ + int ret = 0; + u32 val; + + ret = readx_poll_timeout(sw_r32, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1, + val, !(val & 0x1), 20, timeout); + if (ret) + pr_err("%s: timeout\n", __func__); + + return ret; +} + +/* Reads a register in a page from the PHY */ +static int rtmdio_838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v, park_page = 0x1f << 15; + int err; + + if (port > 31) { + *val = 0xffff; + return 0; + } + + if (page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + sw_w32_mask(0xffff0000, port << 16, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3; + sw_w32(v | park_page, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + + err = rtmdio_838x_smi_wait_op(100000); + if (err) + goto errout; + + *val = sw_r32(RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Write to a register in a page of the PHY */ +static int rtmdio_838x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v, park_page = 0x1f << 15; + int err; + + val &= 0xffff; + if (port > 31 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + sw_w32(BIT(port), RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff0000, val << 16, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2); + + v = reg << 20 | page << 3 | 0x4; + sw_w32(v | park_page, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + + err = rtmdio_838x_smi_wait_op(100000); + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Read an mmd register of a PHY */ +static int rtmdio_838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) +{ + int err; + u32 v; + + mutex_lock(&rtmdio_lock); + + sw_w32(1 << port, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff0000, port << 16, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2); + + v = addr << 16 | reg; + sw_w32(v, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3); + + /* mmd-access | read | cmd-start */ + v = 1 << 1 | 0 << 2 | 1; + sw_w32(v, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + + err = rtmdio_838x_smi_wait_op(100000); + if (err) + goto errout; + + *val = sw_r32(RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Write to an mmd register of a PHY */ +static int rtmdio_838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) +{ + int err; + u32 v; + + pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); + val &= 0xffff; + mutex_lock(&rtmdio_lock); + + sw_w32(1 << port, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff0000, val << 16, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2); + sw_w32_mask(0x1f << 16, addr << 16, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3); + sw_w32_mask(0xffff, reg, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3); + /* mmd-access | write | cmd-start */ + v = 1 << 1 | 1 << 2 | 1; + sw_w32(v, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1); + + err = rtmdio_838x_smi_wait_op(100000); + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* + * The RTL839x has 14 SerDes starting at 0xbb00a000. 0-7, 10, 11 are 5GBit, 8, 9, 12, 13 are + * 10 GBit. Two adjacent SerDes are tightly coupled and share a 1024 bytes register area. Per 32 + * bit address two registers are stored. The first register is stored in the lower 2 bytes ("on + * the right" due to big endian) and the second register in the upper 2 bytes. The following + * register areas are known: + * + * - XSG0 (4 pages @ offset 0x000): for even SerDes + * - XSG1 (4 pages @ offset 0x100): for odd SerDes + * - TGRX (4 pages @ offset 0x200): for even 10G SerDes + * - ANA_RG (2 pages @ offset 0x300): for even 5G SerDes + * - ANA_RG (2 pages @ offset 0x380): for odd 5G SerDes + * - ANA_TG (2 pages @ offset 0x300): for even 10G SerDes + * - ANA_TG (2 pages @ offset 0x380): for odd 10G SerDes + * + * The most consistent mapping that aligns to the RTL93xx devices is: + * + * even 5G SerDes odd 5G SerDes even 10G SerDes odd 10G SerDes + * Page 0: XSG0/0 XSG1/0 XSG0/0 XSG1/0 + * Page 1: XSG0/1 XSG1/1 XSG0/1 XSG1/1 + * Page 2: XSG0/2 XSG1/2 XSG0/2 XSG1/2 + * Page 3: XSG0/3 XSG1/3 XSG0/3 XSG1/3 + * Page 4: TGRX/0 + * Page 5: TGRX/1 + * Page 6: TGRX/2 + * Page 7: TGRX/3 + * Page 8: ANA_RG ANA_RG + * Page 9: ANA_RG_EXT ANA_RG_EXT + * Page 10: ANA_TG ANA_TG + * Page 11: ANA_TG_EXT ANA_TG_EXT + */ + +static int rtmdio_839x_reg_offset(int sds, int page, int regnum) +{ + int offset = ((sds & 0xfe) << 9) + ((regnum & 0xfe) << 1) + (page << 6); + int sds5g = (GENMASK(11, 10) | GENMASK(7, 0)) & BIT(sds); + + if (sds < 0 || sds > 13 || page < 0 || page > 11 || regnum < 0 || regnum > 31) + return -EIO; + + if (page < 4) + return offset + ((sds & 1) << 8); + else if ((page & 4) && (sds == 8 || sds == 12)) + return offset + 0x100; + else if (page >= 8 && page <= 9 && sds5g) + return offset + 0x100 + ((sds & 1) << 7); + else if (page >= 10 && !sds5g) + return offset + 0x80 + ((sds & 1) << 7); + + return -EINVAL; /* hole */ +} + +static int rtmdio_839x_read_sds_phy(int sds, int page, int regnum) +{ + int bitpos = ((regnum << 4) & 0x10); + int offset; + u32 val; + + offset = rtmdio_839x_reg_offset(sds, page, regnum); + if (offset == -EINVAL) + return 0; + + if (offset < 0) + return offset; + + /* phy id is empty so simulate one */ + if (page == 2 && regnum == 2) + return 0x1c; + if (page == 2 && regnum == 3) + return 0x8393; + + val = sw_r32(RTMDIO_839X_BASE + offset); + val = (val >> bitpos) & 0xffff; + + return val; +} + +static int rtmdio_839x_write_sds_phy(int sds, int page, int regnum, u16 val) +{ + u32 neighbor; + int offset; + u32 set; + + offset = rtmdio_839x_reg_offset(sds, page, regnum); + if (offset == -EINVAL) + return 0; + + if (offset < 0) + return 0; + + neighbor = rtmdio_839x_read_sds_phy(sds, page, regnum ^ 1); + if (regnum & 1) + set = (val << 16) + neighbor; + else + set = (neighbor << 16) + val; + + sw_w32(set, RTMDIO_839X_BASE + offset); + + return 0; +} + +/* RTL839x specific MDIO functions */ + +static int rtmdio_839x_smi_wait_op(int timeout) +{ + int ret = 0; + u32 val; + + ret = readx_poll_timeout(sw_r32, RTMDIO_839X_PHYREG_ACCESS_CTRL, + val, !(val & 0x1), 20, timeout); + if (ret) + pr_err("%s: timeout\n", __func__); + + return ret; +} + +static int rtmdio_839x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + int err = 0; + u32 v; + + if (port >= RTMDIO_839X_CPU_PORT || page > 8191 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + sw_w32_mask(0xffff0000, port << 16, RTMDIO_839X_PHYREG_DATA_CTRL); + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTMDIO_839X_PHYREG_CTRL); + + v |= 1; + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + err = rtmdio_839x_smi_wait_op(100000); + if (err) + goto errout; + + *val = sw_r32(RTMDIO_839X_PHYREG_DATA_CTRL) & 0xffff; + +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +static int rtmdio_839x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + int err = 0; + u32 v; + + val &= 0xffff; + if (port >= RTMDIO_839X_CPU_PORT || page > 8191 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access */ + sw_w32(BIT_ULL(port), RTMDIO_839X_PHYREG_PORT_CTRL); + sw_w32(BIT_ULL(port) >> 32, RTMDIO_839X_PHYREG_PORT_CTRL + 4); + + sw_w32_mask(0xffff0000, val << 16, RTMDIO_839X_PHYREG_DATA_CTRL); + + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTMDIO_839X_PHYREG_CTRL); + + v |= BIT(3) | 1; /* Write operation and execute */ + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + err = rtmdio_839x_smi_wait_op(100000); + if (err) + goto errout; + + if (sw_r32(RTMDIO_839X_PHYREG_ACCESS_CTRL) & 0x2) + err = -EIO; + +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Read an mmd register of the PHY */ +static int rtmdio_839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + /* Take bug on RTL839x Rev <= C into account */ + if (port >= RTMDIO_839X_CPU_PORT) + return -EIO; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access */ + sw_w32_mask(0xffff << 16, port << 16, RTMDIO_839X_PHYREG_DATA_CTRL); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | (regnum & 0xffff), RTMDIO_839X_PHYREG_MMD_CTRL); + + v = BIT(2) | BIT(0); /* MMD-access | EXEC */ + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + err = rtmdio_839x_smi_wait_op(100000); + if (err) + goto errout; + + /* There is no error-checking via BIT 1 of v, as it does not seem to be set correctly */ + *val = (sw_r32(RTMDIO_839X_PHYREG_DATA_CTRL) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Write to an mmd register of the PHY */ +static int rtmdio_839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + /* Take bug on RTL839x Rev <= C into account */ + if (port >= RTMDIO_839X_CPU_PORT) + return -EIO; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access */ + sw_w32(BIT_ULL(port), RTMDIO_839X_PHYREG_PORT_CTRL); + sw_w32(BIT_ULL(port) >> 32, RTMDIO_839X_PHYREG_PORT_CTRL + 4); + + /* Set data to write */ + sw_w32_mask(0xffff << 16, val << 16, RTMDIO_839X_PHYREG_DATA_CTRL); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | (regnum & 0xffff), RTMDIO_839X_PHYREG_MMD_CTRL); + + v = BIT(3) | BIT(2) | BIT(0); /* WRITE | MMD-access | EXEC */ + sw_w32(v, RTMDIO_839X_PHYREG_ACCESS_CTRL); + + err = rtmdio_839x_smi_wait_op(100000); + if (err) + goto errout; + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + +errout: + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* + * The RTL930x family has 12 SerDes of three types. They are accessed through two IO registers at + * 0xbb0003b0 which simulate commands to an internal MDIO bus: + * + * - SerDes 0-1 exist on the RTL9301 and 9302B and are QSGMII capable + * - SerDes 2-9 are USXGMII capabable with either quad or single configuration + * - SerDes 10-11 are 10GBase-R capable + */ + +int rtmdio_930x_read_sds_phy(int sds, int page, int regnum) +{ + int i, ret = -EIO; + u32 cmd; + + if (sds < 0 || sds > 11 || page < 0 || page > 63 || regnum < 0 || regnum > 31) + return -EIO; + + mutex_lock(&rtmdio_lock_sds); + + cmd = sds << 2 | page << 7 | regnum << 13 | 1; + sw_w32(cmd, RTMDIO_930X_SDS_INDACS_CMD); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTMDIO_930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + if (i < 100) + ret = sw_r32(RTMDIO_930X_SDS_INDACS_DATA) & 0xffff; + + mutex_unlock(&rtmdio_lock_sds); + + return ret; +} + +int rtmdio_930x_write_sds_phy(int sds, int page, int regnum, u16 val) +{ + int i, ret = -EIO; + u32 cmd; + + if (sds < 0 || sds > 11 || page < 0 || page > 63 || regnum < 0 || regnum > 31) + return -EIO; + + mutex_lock(&rtmdio_lock_sds); + + cmd = sds << 2 | page << 7 | regnum << 13 | 0x3; + sw_w32(val, RTMDIO_930X_SDS_INDACS_DATA); + sw_w32(cmd, RTMDIO_930X_SDS_INDACS_CMD); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTMDIO_930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + mutex_unlock(&rtmdio_lock_sds); + + if (i < 100) + ret = 0; + + return ret; +} + +/* RTL930x specific MDIO functions */ + +static int rtmdio_930x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, val); + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + val &= 0xffff; + mutex_lock(&rtmdio_lock); + + sw_w32(BIT(port), RTMDIO_930X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff << 16, val << 16, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | BIT(2) | BIT(0); + sw_w32(v, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & 0x1); + + if (v & 0x2) + err = -EIO; + + mutex_unlock(&rtmdio_lock); + + return err; +} + +static int rtmdio_930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + int err = 0; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + sw_w32_mask(0xffff << 16, port << 16, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | 1; + sw_w32(v, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + } while ( v & 0x1); + + if (v & BIT(25)) { + pr_debug("Error reading phy %d, register %d\n", port, reg); + err = -EIO; + } + *val = (sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val); + + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Write to an mmd register of the PHY */ +static int rtmdio_930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access */ + sw_w32(BIT(port), RTMDIO_930X_SMI_ACCESS_PHY_CTRL_0); + + /* Set data to write */ + sw_w32_mask(0xffff << 16, val << 16, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | (regnum & 0xffff), RTMDIO_930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(2) | BIT(1) | BIT(0); /* WRITE | MMD-access | EXEC */ + sw_w32(v, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & BIT(0)); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + mutex_unlock(&rtmdio_lock); + return err; +} + +/* Read an mmd register of the PHY */ +static int rtmdio_930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access */ + sw_w32_mask(0xffff << 16, port << 16, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | (regnum & 0xffff), RTMDIO_930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(1) | BIT(0); /* MMD-access | EXEC */ + sw_w32(v, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & BIT(0)); + /* There is no error-checking via BIT 25 of v, as it does not seem to be set correctly */ + *val = (sw_r32(RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* + * The RTL931x family has 14 "frontend" SerDes that are cascaded. All operations (e.g. reset) work + * on this frontend view while their registers are distributed over a total of least 26 background + * SerDes with 64 pages and 32 registers. Three types of SerDes exist: + * + * - Serdes 0,1 are "simple" and work on one background serdes. + * - "Even" SerDes with numbers 2, 4, 6, 8, 10, 12 work on two background SerDes. One analog and + * one digital. + * - "Odd" SerDes with numbers 3, 5, 7, 9, 11, 13 work on a total of 3 background SerDes (one analog + * and two digital) + * + * This maps to: + * + * Frontend SerDes | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + * -----------------+------------------------------------------ + * Backend SerDes 1 | 0 1 2 3 6 7 10 11 14 15 18 19 22 23 + * Backend SerDes 2 | 0 1 2 4 6 8 10 12 14 16 18 20 22 24 + * Backend SerDes 3 | 0 1 3 5 7 9 11 13 15 17 19 21 23 25 + * + * Note: In Realtek proprietary XSGMII mode (10G pumped SGMII) the frontend SerDes works on the + * two digital SerDes while in all other modes it works on the analog and the first digital SerDes. + * Overlapping (e.g. backend SerDes 7 can be analog or digital 2) is avoided by the existing + * hardware designs. + * + * Align this for readability by simulating a total of 576 pages and mix them as follows. + * + * frontend page "even" frontend SerDes "odd" frontend SerDes + * page 0x000-0x03f (analog): page 0x000-0x03f back SDS page 0x000-0x03f back SDS + * page 0x100-0x13f (digi 1): page 0x000-0x03f back SDS page 0x000-0x03f back SDS+1 + * page 0x200-0x23f (digi 2): page 0x000-0x03f back SDS+1 page 0x000-0x03f back SDS+2 + */ + +static int rtmdio_931x_get_backing_sds(u32 sds, u32 page) +{ + int map[] = {0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23}; + int back = map[sds]; + + if (page & 0xc0) + return -EINVAL; /* hole */ + + if (sds >= 2) { + if (sds & 1) + back += (page >> 8); /* distribute "odd" to 3 background SerDes */ + else + back += (page >> 9); /* distribute "even" to 2 background SerDes */ + } + + return back; +} + +static int rtmdio_931x_read_sds_phy(int sds, int page, int regnum) +{ + u32 cmd = sds << 2 | page << 7 | regnum << 13 | 1; + int i, ret = -EIO; + + pr_debug("%s: phy_addr(SDS-ID) %d, phy_reg: %d\n", __func__, sds, regnum); + + mutex_lock(&rtmdio_lock_sds); + sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) + break; + mdelay(1); + } + + if (i < 100) + ret = sw_r32(RTMDIO_931X_SERDES_INDRT_DATA_CTRL) & 0xffff; + + mutex_unlock(&rtmdio_lock_sds); + + pr_debug("%s: returning %08x\n", __func__, ret); + + return ret; +} + +int rtmdio_931x_read_sds_phy_new(int sds, int page, int regnum) +{ + int backsds = rtmdio_931x_get_backing_sds(sds, page); + + return backsds < 0 ? 0 : rtmdio_931x_read_sds_phy(backsds, page & 0x3f, regnum); +} + +static int rtmdio_931x_write_sds_phy(int sds, int page, int regnum, u16 val) +{ + u32 cmd = sds << 2 | page << 7 | regnum << 13;; + int i, ret = -EIO; + + mutex_lock(&rtmdio_lock_sds); + sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); + sw_w32(val, RTMDIO_931X_SERDES_INDRT_DATA_CTRL); + + cmd = sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) | 0x3; + sw_w32(cmd, RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTMDIO_931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) + break; + mdelay(1); + } + + mutex_unlock(&rtmdio_lock_sds); + + if (i < 100) + ret = 0; + + return ret; +} + +int rtmdio_931x_write_sds_phy_new(int sds, int page, int regnum, u16 val) +{ + int backsds = rtmdio_931x_get_backing_sds(sds, page); + + return backsds < 0 ? 0 : rtmdio_931x_write_sds_phy(backsds, page & 0x3f, regnum, val); +} + +/* RTL931x specific MDIO functions */ + +static int rtmdio_931x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + val &= 0xffff; + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + pr_debug("%s: writing to phy %d %d %d %d\n", __func__, port, page, reg, val); + /* Clear both port registers */ + sw_w32(0, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2); + sw_w32(0, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 + 4); + sw_w32_mask(0, BIT(port % 32), RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 + (port / 32) * 4); + + sw_w32_mask(0xffff, val, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3); + + v = reg << 6 | page << 11 ; + sw_w32(v, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + + sw_w32(0x1ff, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_1); + + v |= BIT(4) | 1; /* Write operation and execute */ + sw_w32(v, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + if (sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0) & 0x2) + err = -EIO; + + mutex_unlock(&rtmdio_lock); + + return err; +} + +static int rtmdio_931x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&rtmdio_lock); + + sw_w32(port << 5, RTMDIO_931X_SMI_INDRT_ACCESS_BC_CTRL); + + v = reg << 6 | page << 11 | 1; + sw_w32(v, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + v = sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + *val = sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3); + *val = (*val & 0xffff0000) >> 16; + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x, v: %08x\n", + __func__, port, page, reg, *val, v); + + mutex_unlock(&rtmdio_lock); + + return 0; +} + +/* Read an mmd register of the PHY */ +static int rtmdio_931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + /* Select PHY register type + * If select 1G/10G MMD register type, registers EXT_PAGE, MAIN_PAGE and REG settings are don’t care. + * 0x0 Normal register (Clause 22) + * 0x1: 1G MMD register (MMD via Clause 22 registers 13 and 14) + * 0x2: 10G MMD register (MMD via Clause 45) + */ + int type = 2; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access via port-number */ + sw_w32(port << 5, RTMDIO_931X_SMI_INDRT_ACCESS_BC_CTRL); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | regnum, RTMDIO_931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = type << 2 | BIT(0); /* MMD-access-type | EXEC */ + sw_w32(v, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + /* Check for error condition */ + if (v & BIT(1)) + err = -EIO; + + *val = sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3) >> 16; + + pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, + port, devnum, regnum, *val, err); + + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* Write to an mmd register of the PHY */ +static int rtmdio_931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + int type = 2; + u64 pm; + + mutex_lock(&rtmdio_lock); + + /* Set PHY to access via port-mask */ + pm = (u64)1 << port; + sw_w32((u32)pm, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2); + sw_w32((u32)(pm >> 32), RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 + 4); + + /* Set data to write */ + sw_w32_mask(0xffff, val, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3); + + /* Set MMD device number and register to write to */ + sw_w32(devnum << 16 | regnum, RTMDIO_931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = BIT(4) | type << 2 | BIT(0); /* WRITE | MMD-access-type | EXEC */ + sw_w32(v, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, + port, devnum, regnum, val, err); + mutex_unlock(&rtmdio_lock); + + return err; +} + +/* These are the core functions of our new Realtek SoC MDIO bus. */ + +static int rtmdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err, val; + + if (addr >= priv->cpu_port) + return -ENODEV; + + err = (*priv->read_mmd_phy)(addr, devnum, regnum, &val); + pr_debug("rd_MMD(adr=%d, dev=%d, reg=%d) = %d, err = %d\n", + addr, devnum, regnum, val, err); + return err ? err : val; +} + +static int rtmdio_map_sds_register(int page, int regnum, int *sds_page, int *sds_regnum) +{ + /* + * For the SerDes PHY simulate a register mapping like common RealTek PHYs do. Always + * keep the common registers 0x00-0x0f in place and map the SerDes registers into the + * upper vendor specific registers 0x10-0x17 according to the page select register + * (0x1f). That gives a register mapping as follows: + * + * +-----------------------+-----------------------+---------------+-----------------+ + * | reg 0x00-0x0f | reg 0x10-0x17 | reg 0x18-0x1e | reg 0x1f | + * +-----------------------+-----------------------+---------------+-----------------+ + * | SerDes fiber page (2) | real SerDes registers | zero | SerDes page | + * | registers 0x00-0x0f | in packages of 8 | | select register | + * +-----------------------+-----------------------+---------------+-----------------+ + */ + + if (regnum < 16) { + *sds_page = 2; + *sds_regnum = regnum; + } else if (regnum < 24) { + *sds_page = page / 4; + *sds_regnum = 8 * (page % 4) + (regnum - 16); + } else + return 0; + + return 1; +} + +static int rtmdio_read_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum) +{ + int ret, sds_page, sds_regnum; + + ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); + if (ret) + ret = priv->read_sds_phy(sds, sds_page, sds_regnum); + pr_debug("rd_SDS(sds=%d, pag=%d, reg=%d) = %d\n", sds, page, regnum, ret); + + return ret; +} + +static int rtmdio_write_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum, u16 val) +{ + int ret, sds_page, sds_regnum; + + ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); + if (ret) + ret = priv->write_sds_phy(sds, sds_page, sds_regnum, val); + pr_debug("wr_SDS(sds=%d, pag=%d, reg=%d, val=%d) err = %d\n", sds, page, regnum, val, ret); + + return ret; +} + +static int rtmdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err, val; + + if (addr >= priv->cpu_port) + return -ENODEV; + + if (regnum == RTMDIO_PAGE_SELECT && priv->page[addr] != priv->rawpage) + return priv->page[addr]; + + priv->raw[addr] = (priv->page[addr] == priv->rawpage); + if ((priv->phy_is_internal[addr]) && (priv->sds_id[addr] >= 0)) + return rtmdio_read_sds_phy(priv, priv->sds_id[addr], + priv->page[addr], regnum); + + err = (*priv->read_phy)(addr, priv->page[addr], regnum, &val); + pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", + addr, priv->page[addr], regnum, val, err); + return err ? err : val; +} + +static int rtmdio_93xx_read(struct mii_bus *bus, int addr, int regnum) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err, val; + + if (addr >= priv->cpu_port) + return -ENODEV; + + if (regnum == RTMDIO_PAGE_SELECT && priv->page[addr] != priv->rawpage) + return priv->page[addr]; + + priv->raw[addr] = (priv->page[addr] == priv->rawpage); + if (priv->phy_is_internal[addr]) { + return rtmdio_931x_read_sds_phy(priv->sds_id[addr], + priv->page[addr], regnum); + } + + err = (*priv->read_phy)(addr, priv->page[addr], regnum, &val); + pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", + addr, priv->page[addr], regnum, val, err); + return err ? err : val; +} + +static int rtmdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err; + + if (addr >= priv->cpu_port) + return -ENODEV; + + err = (*priv->write_mmd_phy)(addr, devnum, regnum, val); + pr_debug("wr_MMD(adr=%d, dev=%d, reg=%d, val=%d) err = %d\n", + addr, devnum, regnum, val, err); + return err; +} + +static int rtmdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err, page; + + if (addr >= priv->cpu_port) + return -ENODEV; + + page = priv->page[addr]; + + if (regnum == RTMDIO_PAGE_SELECT) + priv->page[addr] = val; + + if (!priv->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == priv->rawpage)) { + priv->raw[addr] = (page == priv->rawpage); + if (priv->phy_is_internal[addr] && priv->sds_id[addr] >= 0) + return rtmdio_write_sds_phy(priv, priv->sds_id[addr], + priv->page[addr], regnum, val); + + err = (*priv->write_phy)(addr, page, regnum, val); + pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", + addr, page, regnum, val, err); + return err; + } + + priv->raw[addr] = false; + return 0; +} + +static int rtmdio_93xx_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct rtmdio_bus_priv *priv = bus->priv; + int err, page; + + if (addr >= priv->cpu_port) + return -ENODEV; + + page = priv->page[addr]; + + if (regnum == RTMDIO_PAGE_SELECT) + priv->page[addr] = val; + + if (!priv->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == priv->rawpage)) { + priv->raw[addr] = (page == priv->rawpage); + if (priv->phy_is_internal[addr]) { + return rtmdio_931x_write_sds_phy(priv->sds_id[addr], + page, regnum, val); + } + + err = (*priv->write_phy)(addr, page, regnum, val); + pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", + addr, page, regnum, val, err); + } + + priv->raw[addr] = false; + return 0; +} + +static int rtmdio_838x_reset(struct mii_bus *bus) +{ + pr_debug("%s called\n", __func__); + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTMDIO_838X_SMI_POLL_CTRL); + + /* Enable PHY control via SoC */ + sw_w32_mask(0, 1 << 15, RTMDIO_838X_SMI_GLB_CTRL); + + /* Probably should reset all PHYs here... */ + return 0; +} + +static int rtmdio_839x_reset(struct mii_bus *bus) +{ + return 0; + + pr_debug("%s called\n", __func__); + /* BUG: The following does not work, but should! */ + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTMDIO_839X_SMI_PORT_POLLING_CTRL); + sw_w32(0x00000000, RTMDIO_839X_SMI_PORT_POLLING_CTRL + 4); + /* Disable PHY polling via SoC */ + sw_w32_mask(1 << 7, 0, RTMDIO_839X_SMI_GLB_CTRL); + + /* Probably should reset all PHYs here... */ + return 0; +} + +u8 mac_type_bit[RTMDIO_930X_CPU_PORT] = {0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, + 8, 8, 8, 8, 10, 10, 10, 10, 12, 15, 18, 21}; + +static int rtmdio_930x_reset(struct mii_bus *bus) +{ + struct rtmdio_bus_priv *priv = bus->priv; + bool uses_usxgmii = false; /* For the Aquantia PHYs */ + bool uses_hisgmii = false; /* For the RTL8221/8226 */ + u32 private_poll_mask = 0; + u32 poll_sel[2] = { 0 }; + u32 poll_ctrl = 0; + u32 c45_mask = 0; + u32 v; + + /* Mapping of port to phy-addresses on an SMI bus */ + for (int i = 0; i < RTMDIO_930X_CPU_PORT; i++) { + int pos; + + if (priv->smi_bus[i] < 0) + continue; + + pos = (i % 6) * 5; + sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, + RTMDIO_930X_SMI_PORT0_5_ADDR + (i / 6) * 4); + + pos = (i * 2) % 32; + poll_sel[i / 16] |= priv->smi_bus[i] << pos; + poll_ctrl |= BIT(20 + priv->smi_bus[i]); + } + + /* Configure which SMI bus is behind which port number */ + sw_w32(poll_sel[0], RTMDIO_930X_SMI_PORT0_15_POLLING_SEL); + sw_w32(poll_sel[1], RTMDIO_930X_SMI_PORT16_27_POLLING_SEL); + + /* Disable POLL_SEL for any SMI bus with a normal PHY (not RTL8295R for SFP+) */ + sw_w32_mask(poll_ctrl, 0, RTMDIO_930X_SMI_GLB_CTRL); + + /* Configure which SMI busses are polled in c45 based on a c45 PHY being on that bus */ + for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) + if (priv->smi_bus_isc45[i]) + c45_mask |= BIT(i + 16); + + pr_info("c45_mask: %08x\n", c45_mask); + sw_w32_mask(GENMASK(19, 16), c45_mask, RTMDIO_930X_SMI_GLB_CTRL); + + /* Set the MAC type of each port according to the PHY-interface */ + /* Values are FE: 2, GE: 3, XGE/2.5G: 0(SERDES) or 1(otherwise), SXGE: 0 */ + v = 0; + for (int i = 0; i < RTMDIO_930X_CPU_PORT; i++) { + switch (priv->interfaces[i]) { + case PHY_INTERFACE_MODE_10GBASER: + break; /* Serdes: Value = 0 */ + case PHY_INTERFACE_MODE_HSGMII: + private_poll_mask |= BIT(i); + fallthrough; + case PHY_INTERFACE_MODE_USXGMII: + v |= BIT(mac_type_bit[i]); + uses_usxgmii = true; + break; + case PHY_INTERFACE_MODE_QSGMII: + private_poll_mask |= BIT(i); + v |= 3 << mac_type_bit[i]; + break; + default: + break; + } + } + sw_w32(v, RTMDIO_930X_SMI_MAC_TYPE_CTRL); + + /* Set the private polling mask for all Realtek PHYs (i.e. not the 10GBit Aquantia ones) */ + sw_w32(private_poll_mask, RTMDIO_930X_SMI_PRVTE_POLLING_CTRL); + + /* The following magic values are found in the port configuration, they seem to + * define different ways of polling a PHY. The below is for the Aquantia PHYs of + * the XGS1250 and the RTL8226 of the XGS1210 + */ + if (uses_usxgmii) { + sw_w32(0x01010000, RTMDIO_930X_SMI_10G_POLLING_REG0_CFG); + sw_w32(0x01E7C400, RTMDIO_930X_SMI_10G_POLLING_REG9_CFG); + sw_w32(0x01E7E820, RTMDIO_930X_SMI_10G_POLLING_REG10_CFG); + } + if (uses_hisgmii) { + sw_w32(0x011FA400, RTMDIO_930X_SMI_10G_POLLING_REG0_CFG); + sw_w32(0x013FA412, RTMDIO_930X_SMI_10G_POLLING_REG9_CFG); + sw_w32(0x017FA414, RTMDIO_930X_SMI_10G_POLLING_REG10_CFG); + } + + pr_debug("%s: RTMDIO_930X_SMI_GLB_CTRL %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_GLB_CTRL)); + pr_debug("%s: RTMDIO_930X_SMI_PORT0_15_POLLING_SEL %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_PORT0_15_POLLING_SEL)); + pr_debug("%s: RTMDIO_930X_SMI_PORT16_27_POLLING_SEL %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_PORT16_27_POLLING_SEL)); + pr_debug("%s: RTMDIO_930X_SMI_MAC_TYPE_CTRL %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_MAC_TYPE_CTRL)); + pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG0_CFG %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_10G_POLLING_REG0_CFG)); + pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG9_CFG %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_10G_POLLING_REG9_CFG)); + pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG10_CFG %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_10G_POLLING_REG10_CFG)); + pr_debug("%s: RTMDIO_930X_SMI_PRVTE_POLLING_CTRL %08x\n", __func__, + sw_r32(RTMDIO_930X_SMI_PRVTE_POLLING_CTRL)); + + return 0; +} + +static int rtmdio_931x_reset(struct mii_bus *bus) +{ + struct rtmdio_bus_priv *priv = bus->priv; + u32 poll_sel[4] = { 0 }; + u32 poll_ctrl = 0; + u32 c45_mask = 0; + + pr_info("%s called\n", __func__); + /* Disable port polling for configuration purposes */ + sw_w32(0, RTMDIO_931X_SMI_PORT_POLLING_CTRL); + sw_w32(0, RTMDIO_931X_SMI_PORT_POLLING_CTRL + 4); + msleep(100); + + /* Mapping of port to phy-addresses on an SMI bus */ + for (int i = 0; i < RTMDIO_931X_CPU_PORT; i++) { + u32 pos; + + if (priv->smi_bus[i] < 0) + continue; + + pos = (i % 6) * 5; + sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, RTMDIO_931X_SMI_PORT_ADDR + (i / 6) * 4); + pos = (i * 2) % 32; + poll_sel[i / 16] |= priv->smi_bus[i] << pos; + poll_ctrl |= BIT(20 + priv->smi_bus[i]); + } + + /* Configure which SMI bus is behind which port number */ + for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) { + pr_info("poll sel %d, %08x\n", i, poll_sel[i]); + sw_w32(poll_sel[i], RTMDIO_931X_SMI_PORT_POLLING_SEL + (i * 4)); + } + + /* Configure which SMI busses */ + pr_info("c45_mask: %08x, RTMDIO_931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTMDIO_931X_SMI_GLB_CTRL0)); + for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) { + /* bus is polled in c45 */ + if (priv->smi_bus_isc45[i]) + c45_mask |= 0x2 << (i * 2); /* Std. C45, non-standard is 0x3 */ + } + + pr_info("c45_mask: %08x, RTL931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTMDIO_931X_SMI_GLB_CTRL0)); + + /* We have a 10G PHY enable polling + * sw_w32(0x01010000, RTL931X_SMI_10GPHY_POLLING_SEL2); + * sw_w32(0x01E7C400, RTL931X_SMI_10GPHY_POLLING_SEL3); + * sw_w32(0x01E7E820, RTL931X_SMI_10GPHY_POLLING_SEL4); + */ + sw_w32_mask(GENMASK(7, 0), c45_mask, RTMDIO_931X_SMI_GLB_CTRL1); + + return 0; +} + +/* + * TODO: This is a tiny leftover from the central SoC include. For now try to detect the + * Realtek SoC automatically. This needs to be changed to a proper DTS compatible in a + * future driver version. + */ +static int rtmdio_get_family(void) +{ + unsigned int val; + + val = sw_r32(RTMDIO_93XX_MODEL_NAME_INFO_REG); + if ((val & 0xfffc0000) == 0x93000000) + return RTMDIO_930X_FAMILY_ID; + if ((val & 0xfffc0000) == 0x93100000) + return RTMDIO_931X_FAMILY_ID; + + val = sw_r32(RTMDIO_839X_MODEL_NAME_INFO_REG); + if ((val & 0xfff80000) == 0x83900000) + return RTMDIO_839X_FAMILY_ID; + + return RTMDIO_838X_FAMILY_ID; +} + +static int rtmdio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtmdio_bus_priv *priv; + struct device_node *dn; + struct mii_bus *bus; + int i, ret, family; + u32 pn; + + family = rtmdio_get_family(); + dev_info(dev, "probing RTL%04x family mdio bus\n", family); + + bus = devm_mdiobus_alloc_size(dev, sizeof(*priv)); + if (!bus) + return -ENOMEM; + + priv = bus->priv; + for (i=0; i < RTMDIO_MAX_PORT; i++) { + priv->page[i] = 0; + priv->raw[i] = false; + } + + switch(family) { + case RTMDIO_838X_FAMILY_ID: + bus->name = "rtl838x-eth-mdio"; + bus->read = rtmdio_read; + bus->write = rtmdio_write; + bus->reset = rtmdio_838x_reset; + priv->read_sds_phy = rtmdio_838x_read_sds_phy; + priv->write_sds_phy = rtmdio_838x_write_sds_phy; + priv->read_mmd_phy = rtmdio_838x_read_mmd_phy; + priv->write_mmd_phy = rtmdio_838x_write_mmd_phy; + priv->read_phy = rtmdio_838x_read_phy; + priv->write_phy = rtmdio_838x_write_phy; + priv->cpu_port = RTMDIO_838X_CPU_PORT; + priv->rawpage = 0xfff; + break; + case RTMDIO_839X_FAMILY_ID: + bus->name = "rtl839x-eth-mdio"; + bus->read = rtmdio_read; + bus->write = rtmdio_write; + bus->reset = rtmdio_839x_reset; + priv->read_sds_phy = rtmdio_839x_read_sds_phy; + priv->write_sds_phy = rtmdio_839x_write_sds_phy; + priv->read_mmd_phy = rtmdio_839x_read_mmd_phy; + priv->write_mmd_phy = rtmdio_839x_write_mmd_phy; + priv->read_phy = rtmdio_839x_read_phy; + priv->write_phy = rtmdio_839x_write_phy; + priv->cpu_port = RTMDIO_839X_CPU_PORT; + priv->rawpage = 0x1fff; + break; + case RTMDIO_930X_FAMILY_ID: + bus->name = "rtl930x-eth-mdio"; + bus->read = rtmdio_read; + bus->write = rtmdio_write; + bus->reset = rtmdio_930x_reset; + priv->read_sds_phy = rtmdio_930x_read_sds_phy; + priv->write_sds_phy = rtmdio_930x_write_sds_phy; + priv->read_mmd_phy = rtmdio_930x_read_mmd_phy; + priv->write_mmd_phy = rtmdio_930x_write_mmd_phy; + priv->read_phy = rtmdio_930x_read_phy; + priv->write_phy = rtmdio_930x_write_phy; + priv->cpu_port = RTMDIO_930X_CPU_PORT; + priv->rawpage = 0xfff; + break; + case RTMDIO_931X_FAMILY_ID: + bus->name = "rtl931x-eth-mdio"; + bus->read = rtmdio_93xx_read; + bus->write = rtmdio_93xx_write; + bus->reset = rtmdio_931x_reset; + priv->read_mmd_phy = rtmdio_931x_read_mmd_phy; + priv->write_mmd_phy = rtmdio_931x_write_mmd_phy; + priv->read_phy = rtmdio_931x_read_phy; + priv->write_phy = rtmdio_931x_write_phy; + priv->cpu_port = RTMDIO_931X_CPU_PORT; + priv->rawpage = 0x1fff; + break; + } + bus->read_c45 = rtmdio_read_c45; + bus->write_c45 = rtmdio_write_c45; + bus->parent = dev; + bus->phy_mask = ~(BIT_ULL(priv->cpu_port) - 1ULL); + + for_each_node_by_name(dn, "ethernet-phy") { + u32 smi_addr[2]; + + if (of_property_read_u32(dn, "reg", &pn)) + continue; + + if (pn >= RTMDIO_MAX_PORT) { + pr_err("%s: illegal port number %d\n", __func__, pn); + return -ENODEV; + } + + if (of_property_read_u32(dn, "sds", &priv->sds_id[pn])) + priv->sds_id[pn] = -1; + else + pr_info("set sds port %d to %d\n", pn, priv->sds_id[pn]); + + if (of_property_read_u32_array(dn, "rtl9300,smi-address", &smi_addr[0], 2)) { + priv->smi_bus[pn] = 0; + priv->smi_addr[pn] = pn; + } else { + priv->smi_bus[pn] = smi_addr[0]; + priv->smi_addr[pn] = smi_addr[1]; + } + + if (priv->smi_bus[pn] >= RTMDIO_MAX_SMI_BUS) { + pr_err("%s: illegal SMI bus number %d\n", __func__, priv->smi_bus[pn]); + return -ENODEV; + } + + priv->phy_is_internal[pn] = of_property_read_bool(dn, "phy-is-integrated"); + + if (priv->phy_is_internal[pn] && priv->sds_id[pn] >= 0) + priv->smi_bus[pn]= -1; + else if (of_device_is_compatible(dn, "ethernet-phy-ieee802.3-c45")) + priv->smi_bus_isc45[priv->smi_bus[pn]] = true; + } + + dn = of_find_compatible_node(NULL, NULL, "realtek,rtl83xx-switch"); + if (!dn) { + dev_err(dev, "No RTL switch node in DTS\n"); + return -ENODEV; + } + + for_each_node_by_name(dn, "port") { + if (of_property_read_u32(dn, "reg", &pn)) + continue; + pr_debug("%s Looking at port %d\n", __func__, pn); + if (pn > priv->cpu_port) + continue; + if (of_get_phy_mode(dn, &priv->interfaces[pn])) + priv->interfaces[pn] = PHY_INTERFACE_MODE_NA; + pr_debug("%s phy mode of port %d is %s\n", + __func__, pn, phy_modes(priv->interfaces[pn])); + } + + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); + ret = devm_of_mdiobus_register(dev, bus, dev->of_node); + + return ret; +} + + +static const struct of_device_id rtmdio_ids[] = { + { .compatible = "realtek,rtl838x-mdio" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtmdio_ids); + +static struct platform_driver rtmdio_driver = { + .probe = rtmdio_probe, + .driver = { + .name = "mdio-rtl-otto", + .of_match_table = rtmdio_ids, + }, +}; + +module_platform_driver(rtmdio_driver); + +/* + * TODO: The below initialization function is only needed because the mdio bus + * is a subnode of the ethernet node. That means detection via platform driver + * will not work out of the box. Until this is solved, populate the platform + * data manually. + */ +static int __init rtmdio_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "realtek,rtl838x-eth"); + if (!np) { + pr_err("realtek,rtl838x-eth compatible device not found\n"); + return -ENODEV; + } + + pr_info("populating rtl838x-mdio device manually\n"); + of_platform_populate(np, NULL, NULL, NULL); + of_node_put(np); + + return 0; +} +module_init(rtmdio_init); + +MODULE_DESCRIPTION("RTL83xx/RTL93xx MDIO driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/patches-6.12/806-add-mdio-driver.patch b/target/linux/realtek/patches-6.12/806-add-mdio-driver.patch new file mode 100644 index 00000000000..17461f0e4b9 --- /dev/null +++ b/target/linux/realtek/patches-6.12/806-add-mdio-driver.patch @@ -0,0 +1,34 @@ +From 5b38f63ee59afd95c1d265b7e2097a0958db8a61 Mon Sep 01 00:00:00 2025 +From: Markus Stockhausen +Date: Mon, 01 Sep 2025 20:13:21 +0200 +Subject: realtek: add support for mdio controller + +Signed-off-by: Markus Stockhausen +--- + +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms + obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o + obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o + obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o ++obj-$(CONFIG_MDIO_REALTEK_OTTO) += mdio-realtek-otto.o + obj-$(CONFIG_MDIO_REALTEK_OTTO_AUX) += mdio-realtek-otto-aux.o + obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -207,6 +207,13 @@ config MDIO_REGMAP + regmap. Users willing to use this driver must explicitly select + REGMAP. + ++config MDIO_REALTEK_OTTO ++ tristate "Realtek Otto MDIO interface support" ++ default MACH_REALTEK_RTL ++ depends on MACH_REALTEK_RTL || COMPILE_TEST ++ help ++ This driver supports the MDIO bus on RTL83xx/RTL93xx SoCs. ++ + config MDIO_REALTEK_OTTO_AUX + tristate "Realtek Otto auxiliary MDIO interface support" + default MACH_REALTEK_RTL diff --git a/target/linux/realtek/rtl838x/config-6.12 b/target/linux/realtek/rtl838x/config-6.12 index a7cd07212ce..287541feb26 100644 --- a/target/linux/realtek/rtl838x/config-6.12 +++ b/target/linux/realtek/rtl838x/config-6.12 @@ -134,6 +134,7 @@ CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_GPIO=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_CORE=y diff --git a/target/linux/realtek/rtl839x/config-6.12 b/target/linux/realtek/rtl839x/config-6.12 index 15a01c87dad..51c0336710e 100644 --- a/target/linux/realtek/rtl839x/config-6.12 +++ b/target/linux/realtek/rtl839x/config-6.12 @@ -132,6 +132,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_CORE=y diff --git a/target/linux/realtek/rtl930x/config-6.12 b/target/linux/realtek/rtl930x/config-6.12 index bbfd2fd7bdb..82880f3a169 100644 --- a/target/linux/realtek/rtl930x/config-6.12 +++ b/target/linux/realtek/rtl930x/config-6.12 @@ -116,6 +116,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y diff --git a/target/linux/realtek/rtl930x_nand/config-6.12 b/target/linux/realtek/rtl930x_nand/config-6.12 index 0f4294ac301..5ba7b2e613a 100644 --- a/target/linux/realtek/rtl930x_nand/config-6.12 +++ b/target/linux/realtek/rtl930x_nand/config-6.12 @@ -116,6 +116,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y diff --git a/target/linux/realtek/rtl931x/config-6.12 b/target/linux/realtek/rtl931x/config-6.12 index 51887043a04..f45a17fe7e4 100644 --- a/target/linux/realtek/rtl931x/config-6.12 +++ b/target/linux/realtek/rtl931x/config-6.12 @@ -121,6 +121,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y diff --git a/target/linux/realtek/rtl931x_nand/config-6.12 b/target/linux/realtek/rtl931x_nand/config-6.12 index e65b6043feb..5bf65a5b8be 100644 --- a/target/linux/realtek/rtl931x_nand/config-6.12 +++ b/target/linux/realtek/rtl931x_nand/config-6.12 @@ -121,6 +121,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y