1
1
openwrt/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c
Markus Stockhausen 45fa6e3175 realtek: mdio: harden mdio probing
Do better error checks during bus probing. Give meaningful return codes
in case of invalid DTS data (EINVAL instead of ENODEV). Decrease node
reference in case of errors.

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/21968
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-02-13 12:53:47 +01:00

1035 lines
35 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/fwnode.h>
#include <linux/fwnode_mdio.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#define RTMDIO_MAX_PORT 57
#define RTMDIO_MAX_SMI_BUS 4
#define RTMDIO_PAGE_SELECT 0x1f
#define RTMDIO_PHY_AQR113C_A 0x31c31c12
#define RTMDIO_PHY_AQR113C_B 0x31c31c13
#define RTMDIO_PHY_AQR813 0x31c31cb2
#define RTMDIO_PHY_RTL8221B_VB_CG 0x001cc849
#define RTMDIO_PHY_RTL8221B_VM_CG 0x001cc84a
#define RTMDIO_PHY_RTL8224 0x001ccad0
#define RTMDIO_PHY_RTL8226 0x001cc838
#define RTMDIO_PHY_RTL8218D 0x001cc983
#define RTMDIO_PHY_RTL8218E 0x001cc984
#define RTMDIO_PHY_MAC_1G 3
#define RTMDIO_PHY_MAC_2G_PLUS 1
#define RTMDIO_PHY_POLL_MMD(dev, reg, bit) ((bit << 21) | (dev << 16) | reg)
/* MDIO bus registers */
#define RTMDIO_RUN BIT(0)
#define RTMDIO_838X_CMD_FAIL 0
#define RTMDIO_838X_CMD_READ_C22 0
#define RTMDIO_838X_CMD_READ_C45 BIT(1)
#define RTMDIO_838X_CMD_WRITE_C22 BIT(2)
#define RTMDIO_838X_CMD_WRITE_C45 BIT(1) | BIT(2)
#define RTMDIO_838X_CMD_MASK GENMASK(2, 0)
#define RTMDIO_838X_PHY_PATCH_DONE BIT(15)
#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_CMD_FAIL BIT(1)
#define RTMDIO_839X_CMD_READ_C22 0
#define RTMDIO_839X_CMD_READ_C45 BIT(2)
#define RTMDIO_839X_CMD_WRITE_C22 BIT(3)
#define RTMDIO_839X_CMD_WRITE_C45 BIT(2) | BIT(3)
#define RTMDIO_839X_CMD_MASK GENMASK(3, 0)
#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_CMD_FAIL BIT(25)
#define RTMDIO_930X_CMD_READ_C22 0
#define RTMDIO_930X_CMD_READ_C45 BIT(1)
#define RTMDIO_930X_CMD_WRITE_C22 BIT(2)
#define RTMDIO_930X_CMD_WRITE_C45 BIT(1) | BIT(2)
#define RTMDIO_930X_CMD_MASK GENMASK(2, 0) | BIT(25)
#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_CMD_FAIL BIT(1)
#define RTMDIO_931X_CMD_READ_C22 0
#define RTMDIO_931X_CMD_READ_C45 BIT(3)
#define RTMDIO_931X_CMD_WRITE_C22 BIT(4)
#define RTMDIO_931X_CMD_WRITE_C45 BIT(3) | BIT(4)
#define RTMDIO_931X_CMD_MASK GENMASK(4, 0)
#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)
#define RTMDIO_931X_SMI_10GPHY_POLLING_SEL0 (0x0CF0)
#define RTMDIO_931X_SMI_10GPHY_POLLING_SEL1 (0x0CF4)
#define RTMDIO_931X_SMI_10GPHY_POLLING_SEL2 (0x0CF8)
#define RTMDIO_931X_SMI_10GPHY_POLLING_SEL3 (0x0CFC)
#define RTMDIO_931X_SMI_10GPHY_POLLING_SEL4 (0x0D00)
/*
* 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.
*/
struct rtmdio_ctrl {
struct regmap *map;
const struct rtmdio_config *cfg;
int page[RTMDIO_MAX_PORT];
bool raw[RTMDIO_MAX_PORT];
int smi_bus[RTMDIO_MAX_PORT];
int smi_addr[RTMDIO_MAX_PORT];
bool smi_bus_isc45[RTMDIO_MAX_SMI_BUS];
};
struct rtmdio_config {
int cpu_port;
int raw_page;
int (*read_mmd_phy)(struct mii_bus *bus, u32 port, u32 addr, u32 reg, u32 *val);
int (*read_phy)(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 *val);
int (*reset)(struct mii_bus *bus);
void (*setup_polling)(struct mii_bus *bus);
int (*write_mmd_phy)(struct mii_bus *bus, u32 port, u32 addr, u32 reg, u32 val);
int (*write_phy)(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 val);
};
struct rtmdio_phy_info {
unsigned int phy_id;
bool phy_unknown;
int mac_type;
bool has_giga_lite;
bool has_res_reg;
bool force_res;
unsigned int poll_duplex;
unsigned int poll_adv_1000;
unsigned int poll_lpa_1000;
};
static int rtmdio_run_cmd(struct mii_bus *bus, int cmd, int mask, int regnum, int fail)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int ret, val;
ret = regmap_update_bits(ctrl->map, regnum, mask, cmd | RTMDIO_RUN);
ret = regmap_read_poll_timeout(ctrl->map, regnum, val, !(val & RTMDIO_RUN), 20, 500000);
if (ret)
WARN_ONCE(1, "mdio bus access timed out\n");
else if (val & fail) {
WARN_ONCE(1, "mdio bus access failed\n");
ret = -EIO;
}
return ret;
}
static int rtmdio_838x_run_cmd(struct mii_bus *bus, int cmd)
{
return rtmdio_run_cmd(bus, cmd, RTMDIO_838X_CMD_MASK,
RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1, RTMDIO_838X_CMD_FAIL);
}
static int rtmdio_838x_read_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u32 park_page = 31;
int err;
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, port << 16);
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1,
reg << 20 | park_page << 15 | page << 3);
err = rtmdio_838x_run_cmd(bus, RTMDIO_838X_CMD_READ_C22);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_838x_write_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u32 park_page = 31;
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, val << 16);
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_1,
reg << 20 | park_page << 15 | page << 3);
return rtmdio_838x_run_cmd(bus, RTMDIO_838X_CMD_WRITE_C22);
}
static int rtmdio_838x_read_mmd_phy(struct mii_bus *bus, u32 port, u32 addr, u32 reg, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err;
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, port << 16);
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3, addr << 16 | reg);
err = rtmdio_838x_run_cmd(bus, RTMDIO_838X_CMD_READ_C45);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_838x_write_mmd_phy(struct mii_bus *bus, u32 port, u32 addr, u32 reg, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_2, val << 16);
regmap_write(ctrl->map, RTMDIO_838X_SMI_ACCESS_PHY_CTRL_3, addr << 16 | reg);
return rtmdio_838x_run_cmd(bus, RTMDIO_838X_CMD_WRITE_C45);
}
static int rtmdio_839x_run_cmd(struct mii_bus *bus, int cmd)
{
return rtmdio_run_cmd(bus, cmd, RTMDIO_839X_CMD_MASK,
RTMDIO_839X_PHYREG_ACCESS_CTRL, RTMDIO_839X_CMD_FAIL);
}
static int rtmdio_839x_read_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err;
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_CTRL, 0x1ff);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, port << 16);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_ACCESS_CTRL,
reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23);
err = rtmdio_839x_run_cmd(bus, RTMDIO_839X_CMD_READ_C22);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_839x_write_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_CTRL, 0x1ff);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, val << 16);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_PORT_CTRL, BIT_ULL(port));
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_PORT_CTRL + 4, BIT_ULL(port) >> 32);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_ACCESS_CTRL,
reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23);
return rtmdio_839x_run_cmd(bus, RTMDIO_839X_CMD_WRITE_C22);
}
static int rtmdio_839x_read_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err;
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, port << 16);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_MMD_CTRL, (devnum << 16) | (regnum & 0xffff));
err = rtmdio_839x_run_cmd(bus, RTMDIO_839X_CMD_READ_C45);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_839x_write_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_PORT_CTRL, BIT_ULL(port));
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_PORT_CTRL + 4, BIT_ULL(port) >> 32);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_DATA_CTRL, val << 16);
regmap_write(ctrl->map, RTMDIO_839X_PHYREG_MMD_CTRL, (devnum << 16) | (regnum & 0xffff));
return rtmdio_839x_run_cmd(bus, RTMDIO_839X_CMD_WRITE_C45);
}
static int rtmdio_930x_run_cmd(struct mii_bus *bus, int cmd)
{
return rtmdio_run_cmd(bus, cmd, RTMDIO_930X_CMD_MASK,
RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1, RTMDIO_930X_CMD_FAIL);
}
static int rtmdio_930x_write_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u32 park_page = 31;
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, val << 16);
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1,
reg << 20 | page << 3 | park_page << 15);
return rtmdio_930x_run_cmd(bus, RTMDIO_930X_CMD_WRITE_C22);
}
static int rtmdio_930x_read_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u32 park_page = 31;
int err;
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, port << 16);
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_1,
reg << 20 | page << 3 | park_page << 15);
err = rtmdio_930x_run_cmd(bus, RTMDIO_930X_CMD_READ_C22);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_930x_write_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_0, BIT(port));
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, val << 16);
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_3, (devnum << 16) | (regnum & 0xffff));
return rtmdio_930x_run_cmd(bus, RTMDIO_930X_CMD_WRITE_C45);
}
static int rtmdio_930x_read_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err ;
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, port << 16);
regmap_write(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_3, (devnum << 16) | (regnum & 0xffff));
err = rtmdio_930x_run_cmd(bus, RTMDIO_930X_CMD_READ_C45);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_930X_SMI_ACCESS_PHY_CTRL_2, val);
if (!err)
*val &= GENMASK(15, 0);
return err;
}
static int rtmdio_931x_run_cmd(struct mii_bus *bus, int cmd)
{
return rtmdio_run_cmd(bus, cmd, RTMDIO_931X_CMD_MASK,
RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0, RTMDIO_931X_CMD_FAIL);
}
static int rtmdio_931x_write_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u64 mask = BIT_ULL(port);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2, (u32)mask);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 + 4, (u32)(mask >> 32));
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3, val);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0, reg << 6 | page << 11);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_1, 0x1ff);
return rtmdio_931x_run_cmd(bus, RTMDIO_931X_CMD_WRITE_C22);
}
static int rtmdio_931x_read_phy(struct mii_bus *bus, u32 port, u32 page, u32 reg, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err;
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_BC_CTRL, port << 5);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_0, reg << 6 | page << 11);
err = rtmdio_931x_run_cmd(bus, RTMDIO_931X_CMD_READ_C22);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3, val);
if (!err)
*val >>= 16;
return err;
}
static int rtmdio_931x_read_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 *val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err;
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_BC_CTRL, port << 5);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_MMD_CTRL, (devnum << 16) | (regnum & 0xffff));
err = rtmdio_931x_run_cmd(bus, RTMDIO_931X_CMD_READ_C45);
if (!err)
err = regmap_read(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3, val);
if (!err)
*val >>= 16;
return err;
}
static int rtmdio_931x_write_mmd_phy(struct mii_bus *bus, u32 port, u32 devnum, u32 regnum, u32 val)
{
struct rtmdio_ctrl *ctrl = bus->priv;
u64 mask = BIT_ULL(port);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2, (u32)mask);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_2 + 4, (u32)(mask >> 32));
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_CTRL_3, val);
regmap_write(ctrl->map, RTMDIO_931X_SMI_INDRT_ACCESS_MMD_CTRL, (devnum << 16) | (regnum & 0xffff));
return rtmdio_931x_run_cmd(bus, RTMDIO_931X_CMD_WRITE_C45);
}
static int rtmdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err, val;
if (addr >= ctrl->cfg->cpu_port)
return -ENODEV;
err = (*ctrl->cfg->read_mmd_phy)(bus, 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_read(struct mii_bus *bus, int addr, int regnum)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int err, val;
if (addr >= ctrl->cfg->cpu_port)
return -ENODEV;
/* prevent WARN_ONCE() during scan */
if (ctrl->smi_bus[addr] >=0 && ctrl->smi_bus_isc45[ctrl->smi_bus[addr]] && regnum == 2)
return -EIO;
if (regnum == RTMDIO_PAGE_SELECT && ctrl->page[addr] != ctrl->cfg->raw_page)
return ctrl->page[addr];
ctrl->raw[addr] = (ctrl->page[addr] == ctrl->cfg->raw_page);
err = (*ctrl->cfg->read_phy)(bus, addr, ctrl->page[addr], regnum, &val);
pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n",
addr, ctrl->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_ctrl *ctrl = bus->priv;
int err;
if (addr >= ctrl->cfg->cpu_port)
return -ENODEV;
err = (*ctrl->cfg->write_mmd_phy)(bus, 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_ctrl *ctrl = bus->priv;
int err, page;
if (addr >= ctrl->cfg->cpu_port)
return -ENODEV;
page = ctrl->page[addr];
if (regnum == RTMDIO_PAGE_SELECT)
ctrl->page[addr] = val;
if (!ctrl->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == ctrl->cfg->raw_page)) {
ctrl->raw[addr] = (page == ctrl->cfg->raw_page);
err = (*ctrl->cfg->write_phy)(bus, 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;
}
ctrl->raw[addr] = false;
return 0;
}
static u32 rtmdio_get_phy_id(struct phy_device *phydev)
{
if (!phydev)
return 0;
if (phydev->is_c45) {
for (int devad = 0; devad < MDIO_MMD_NUM; devad++) {
u32 phyid = phydev->c45_ids.device_ids[devad];
if (phyid && phyid != 0xffffffff)
return phyid;
}
}
return phydev->phy_id;
}
static void rtmdio_get_phy_info(struct mii_bus *bus, int addr, struct rtmdio_phy_info *phyinfo)
{
struct phy_device *phydev = mdiobus_get_phy(bus, addr);
u32 phyid = rtmdio_get_phy_id(phydev);
/*
* Depending on the attached PHY the polling mechanism must be fine tuned. Basically
* this boils down to which registers must be read and if there are any special
* features.
*/
memset(phyinfo, 0, sizeof(*phyinfo));
switch(phyid) {
case RTMDIO_PHY_AQR113C_A:
case RTMDIO_PHY_AQR113C_B:
case RTMDIO_PHY_AQR813:
phyinfo->mac_type = RTMDIO_PHY_MAC_2G_PLUS;
phyinfo->poll_duplex = RTMDIO_PHY_POLL_MMD(1, 0x0000, 8);
phyinfo->poll_adv_1000 = RTMDIO_PHY_POLL_MMD(7, 0xc400, 15);
phyinfo->poll_lpa_1000 = RTMDIO_PHY_POLL_MMD(7, 0xe820, 15);
break;
case RTMDIO_PHY_RTL8218D:
case RTMDIO_PHY_RTL8218E:
phyinfo->mac_type = RTMDIO_PHY_MAC_1G;
phyinfo->has_giga_lite = true;
break;
case RTMDIO_PHY_RTL8226:
case RTMDIO_PHY_RTL8221B_VB_CG:
case RTMDIO_PHY_RTL8221B_VM_CG:
case RTMDIO_PHY_RTL8224:
phyinfo->mac_type = RTMDIO_PHY_MAC_2G_PLUS;
phyinfo->has_giga_lite = true;
phyinfo->poll_duplex = RTMDIO_PHY_POLL_MMD(31, 0xa400, 8);
phyinfo->poll_adv_1000 = RTMDIO_PHY_POLL_MMD(31, 0xa412, 9);
phyinfo->poll_lpa_1000 = RTMDIO_PHY_POLL_MMD(31, 0xa414, 11);
break;
default:
phyinfo->phy_unknown = true;
break;
}
}
static int rtmdio_838x_reset(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
/*
* PHY_PATCH_DONE enables phy control via SoC. This is required for phy access,
* including patching. Must always be set before the phys are probed.
*/
regmap_update_bits(ctrl->map, RTMDIO_838X_SMI_GLB_CTRL,
RTMDIO_838X_PHY_PATCH_DONE, RTMDIO_838X_PHY_PATCH_DONE);
return 0;
}
static void rtmdio_838x_setup_polling(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
int combo_phy;
/* Disable MAC polling for PHY config. It will be activated later in the DSA driver */
regmap_write(ctrl->map, RTMDIO_838X_SMI_POLL_CTRL, 0);
/*
* Control bits EX_PHY_MAN_xxx have an important effect on the detection of the media
* status (fibre/copper) of a PHY. Once activated, register MAC_LINK_MEDIA_STS can
* give the real media status (0=copper, 1=fibre). For now assume that if port 24 is
* PHY driven, it must be a combo PHY and media detection is needed.
*/
combo_phy = ctrl->smi_bus[24] < 0 ? 0 : BIT(7);
regmap_update_bits(ctrl->map, RTMDIO_838X_SMI_GLB_CTRL, BIT(7), combo_phy);
}
static int rtmdio_839x_reset(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
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 */
regmap_write(ctrl->map, RTMDIO_839X_SMI_PORT_POLLING_CTRL, 0);
regmap_write(ctrl->map, RTMDIO_839X_SMI_PORT_POLLING_CTRL + 4, 0);
/* Disable PHY polling via SoC */
regmap_update_bits(ctrl->map, RTMDIO_839X_SMI_GLB_CTRL, BIT(7), 0);
/* Probably should reset all PHYs here... */
return 0;
}
static int rtmdio_930x_reset(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
unsigned int reg, mask, val;
/* Define bus topology */
for (int addr = 0; addr < ctrl->cfg->cpu_port; addr++) {
if (ctrl->smi_bus[addr] < 0)
continue;
reg = (addr / 6) * 4;
mask = 0x1f << ((addr % 6) * 5);
val = ctrl->smi_addr[addr] << (ffs(mask) - 1);
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_PORT0_5_ADDR + reg, mask, val);
reg = (addr / 16) * 4;
mask = 0x3 << ((addr % 16) * 2);
val = ctrl->smi_bus[addr] << (ffs(mask) - 1);
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_PORT0_15_POLLING_SEL + reg, mask, val);
}
/* Define C22/C45 bus feature set */
for (int addr = 0; addr < RTMDIO_MAX_SMI_BUS; addr++) {
mask = BIT(16 + addr);
val = ctrl->smi_bus_isc45[addr] ? mask : 0;
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_GLB_CTRL, mask, val);
}
return 0;
}
static void rtmdio_930x_setup_polling(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
struct rtmdio_phy_info phyinfo;
unsigned int mask, val;
/* Define PHY specific polling parameters */
for (int addr = 0; addr < ctrl->cfg->cpu_port; addr++) {
if (ctrl->smi_bus[addr] < 0)
continue;
rtmdio_get_phy_info(bus, addr, &phyinfo);
if (phyinfo.phy_unknown) {
pr_warn("skip polling setup for unknown PHY %08x on port %d\n",
phyinfo.phy_id, addr);
continue;
}
/* port MAC type */
mask = addr > 23 ? 0x7 << ((addr - 24) * 3 + 12): 0x3 << ((addr / 4) * 2);
val = phyinfo.mac_type << (ffs(mask) - 1);
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_MAC_TYPE_CTRL, mask, val);
/* polling via standard or resolution register */
mask = BIT(20 + ctrl->smi_bus[addr]);
val = phyinfo.has_res_reg ? mask : 0;
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_GLB_CTRL, mask, val);
/* proprietary Realtek 1G/2.5 lite polling */
mask = BIT(addr);
val = phyinfo.has_giga_lite ? mask : 0;
regmap_update_bits(ctrl->map, RTMDIO_930X_SMI_PRVTE_POLLING_CTRL, mask, val);
/* special duplex/advertisement polling registers */
if (phyinfo.poll_duplex || phyinfo.poll_adv_1000 || phyinfo.poll_lpa_1000) {
regmap_write(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG0_CFG, phyinfo.poll_duplex);
regmap_write(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG9_CFG, phyinfo.poll_adv_1000);
regmap_write(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG10_CFG, phyinfo.poll_lpa_1000);
}
}
regmap_read(ctrl->map, RTMDIO_930X_SMI_GLB_CTRL, &val);
pr_debug("%s: RTMDIO_930X_SMI_GLB_CTRL %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_PORT0_15_POLLING_SEL, &val);
pr_debug("%s: RTMDIO_930X_SMI_PORT0_15_POLLING_SEL %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_PORT16_27_POLLING_SEL, &val);
pr_debug("%s: RTMDIO_930X_SMI_PORT16_27_POLLING_SEL %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_MAC_TYPE_CTRL, &val);
pr_debug("%s: RTMDIO_930X_SMI_MAC_TYPE_CTRL %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG0_CFG, &val);
pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG0_CFG %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG9_CFG, &val);
pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG9_CFG %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_10G_POLLING_REG10_CFG, &val);
pr_debug("%s: RTMDIO_930X_SMI_10G_POLLING_REG10_CFG %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_930X_SMI_PRVTE_POLLING_CTRL, &val);
pr_debug("%s: RTMDIO_930X_SMI_PRVTE_POLLING_CTRL %08x\n", __func__, val);
}
static int rtmdio_931x_reset(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = 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 */
regmap_write(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_CTRL, 0);
regmap_write(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_CTRL + 4, 0);
msleep(100);
/* Mapping of port to phy-addresses on an SMI bus */
for (int addr = 0; addr < ctrl->cfg->cpu_port; addr++) {
u32 pos;
if (ctrl->smi_bus[addr] < 0)
continue;
pos = (addr % 6) * 5;
regmap_update_bits(ctrl->map, RTMDIO_931X_SMI_PORT_ADDR + (addr / 6) * 4,
0x1f << pos, ctrl->smi_addr[addr] << pos);
pos = (addr * 2) % 32;
poll_sel[addr / 16] |= ctrl->smi_bus[addr] << pos;
poll_ctrl |= BIT(20 + ctrl->smi_bus[addr]);
}
/* 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]);
regmap_write(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_SEL + (i * 4), poll_sel[i]);
}
/* Define C22/C45 bus feature set */
for (int i = 0; i < RTMDIO_MAX_SMI_BUS; i++) {
if (ctrl->smi_bus_isc45[i])
c45_mask |= 0x2 << (i * 2); /* Std. C45, non-standard is 0x3 */
}
pr_info("%s: c45_mask: %08x", __func__, c45_mask);
regmap_update_bits(ctrl->map, RTMDIO_931X_SMI_GLB_CTRL1, GENMASK(7, 0), c45_mask);
return 0;
}
static void rtmdio_931x_setup_polling(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
struct rtmdio_phy_info phyinfo;
u32 val;
/* Define PHY specific polling parameters
*
* Those are applied per port here but the SoC only supports them
* per SMI bus or for all GPHY/10GPHY. This should be guarded by
* the existing hardware designs (i.e. only equally polled PHYs on
* the same SMI bus or kind of PHYs).
*/
for (int addr = 0; addr < ctrl->cfg->cpu_port; addr++) {
unsigned int mask, val;
int smi = ctrl->smi_bus[addr];
if (smi < 0)
continue;
rtmdio_get_phy_info(bus, addr, &phyinfo);
if (phyinfo.phy_unknown) {
pr_warn("skip polling setup for unknown PHY %08x on port %d\n",
phyinfo.phy_id, addr);
continue;
}
mask = val = 0;
/* PRVTE0 polling */
mask |= BIT(20 + smi);
if (phyinfo.has_res_reg)
val |= BIT(20 + smi);
/* PRVTE1 polling */
mask |= BIT(24 + smi);
if (phyinfo.force_res)
val |= BIT(24 + smi);
regmap_update_bits(ctrl->map, RTMDIO_931X_SMI_GLB_CTRL0, mask, val);
/* polling std. or proprietary format (bit 0 of SMI_SETX_FMT_SEL) */
mask = BIT(smi * 2);
val = phyinfo.force_res ? mask : 0;
regmap_update_bits(ctrl->map, RTMDIO_931X_SMI_GLB_CTRL1, mask, val);
/* special polling registers */
if (phyinfo.poll_duplex || phyinfo.poll_adv_1000 || phyinfo.poll_lpa_1000) {
regmap_write(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL2, phyinfo.poll_duplex);
regmap_write(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL3, phyinfo.poll_adv_1000);
regmap_write(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL4, phyinfo.poll_lpa_1000);
}
}
regmap_read(ctrl->map, RTMDIO_931X_SMI_GLB_CTRL0, &val);
pr_debug("%s: RTMDIO_931X_SMI_GLB_CTRL0 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_GLB_CTRL1, &val);
pr_debug("%s: RTMDIO_931X_SMI_GLB_CTRL1 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_SEL, &val);
pr_debug("%s: RTMDIO_931X_SMI_PORT_POLLING_SEL_0_15 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_SEL + 4, &val);
pr_debug("%s: RTMDIO_931X_SMI_PORT_POLLING_SEL_16_27 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_SEL + 8, &val);
pr_debug("%s: RTMDIO_931X_SMI_PORT_POLLING_SEL_28_43 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_PORT_POLLING_SEL + 12, &val);
pr_debug("%s: RTMDIO_931X_SMI_PORT_POLLING_SEL_44_55 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL0, &val);
pr_debug("%s: RTMDIO_931X_SMI_10GPHY_POLLING_SEL0 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL1, &val);
pr_debug("%s: RTMDIO_931X_SMI_10GPHY_POLLING_SEL1 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL2, &val);
pr_debug("%s: RTMDIO_931X_SMI_10GPHY_POLLING_SEL2 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL3, &val);
pr_debug("%s: RTMDIO_931X_SMI_10GPHY_POLLING_SEL3 %08x\n", __func__, val);
regmap_read(ctrl->map, RTMDIO_931X_SMI_10GPHY_POLLING_SEL4, &val);
pr_debug("%s: RTMDIO_931X_SMI_10GPHY_POLLING_SEL4 %08x\n", __func__, val);
}
static int rtmdio_reset(struct mii_bus *bus)
{
struct rtmdio_ctrl *ctrl = bus->priv;
return ctrl->cfg->reset(bus);
}
static int rtmdio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtmdio_ctrl *ctrl;
struct device_node *dn;
struct mii_bus *bus;
u64 mask = 0ULL;
int ret, addr;
bus = devm_mdiobus_alloc_size(dev, sizeof(*ctrl));
if (!bus)
return -ENOMEM;
ctrl = bus->priv;
ctrl->cfg = (const struct rtmdio_config *)device_get_match_data(dev);
ctrl->map = syscon_node_to_regmap(pdev->dev.of_node->parent);
if (IS_ERR(ctrl->map))
return PTR_ERR(ctrl->map);
for (addr = 0; addr < RTMDIO_MAX_PORT; addr++)
ctrl->smi_bus[addr] = -1;
for_each_node_by_name(dn, "ethernet-phy") {
if (of_property_read_u32(dn, "reg", &addr))
continue;
if (addr < 0 || addr >= ctrl->cfg->cpu_port) {
dev_err(dev, "illegal port number %d\n", addr);
of_node_put(dn);
return -EINVAL;
}
of_property_read_u32(dn->parent, "reg", &ctrl->smi_bus[addr]);
if (of_property_read_u32(dn, "realtek,smi-address", &ctrl->smi_addr[addr]))
ctrl->smi_addr[addr] = addr;
if (ctrl->smi_bus[addr] < 0 || ctrl->smi_bus[addr] >= RTMDIO_MAX_SMI_BUS) {
dev_err(dev, "illegal SMI bus number %d\n", ctrl->smi_bus[addr]);
of_node_put(dn);
return -EINVAL;
}
if (of_device_is_compatible(dn, "ethernet-phy-ieee802.3-c45"))
ctrl->smi_bus_isc45[ctrl->smi_bus[addr]] = true;
mask |= BIT_ULL(addr);
}
bus->name = "Realtek MDIO bus";
bus->reset = rtmdio_reset;
bus->read = rtmdio_read;
bus->write = rtmdio_write;
bus->read_c45 = rtmdio_read_c45;
bus->write_c45 = rtmdio_write_c45;
bus->parent = dev;
bus->phy_mask = ~mask;
snprintf(bus->id, MII_BUS_ID_SIZE, "realtek-mdio");
device_set_node(&bus->dev, of_fwnode_handle(dev->of_node));
ret = devm_mdiobus_register(dev, bus);
if (ret)
return ret;
if (ctrl->cfg->setup_polling)
ctrl->cfg->setup_polling(bus);
return 0;
}
static const struct rtmdio_config rtmdio_838x_cfg = {
.cpu_port = 28,
.raw_page = 4095,
.read_mmd_phy = rtmdio_838x_read_mmd_phy,
.read_phy = rtmdio_838x_read_phy,
.reset = rtmdio_838x_reset,
.setup_polling = rtmdio_838x_setup_polling,
.write_mmd_phy = rtmdio_838x_write_mmd_phy,
.write_phy = rtmdio_838x_write_phy,
};
static const struct rtmdio_config rtmdio_839x_cfg = {
.cpu_port = 52,
.raw_page = 8191,
.read_mmd_phy = rtmdio_839x_read_mmd_phy,
.read_phy = rtmdio_839x_read_phy,
.reset = rtmdio_839x_reset,
.write_mmd_phy = rtmdio_839x_write_mmd_phy,
.write_phy = rtmdio_839x_write_phy,
};
static const struct rtmdio_config rtmdio_930x_cfg = {
.cpu_port = 28,
.raw_page = 4095,
.read_mmd_phy = rtmdio_930x_read_mmd_phy,
.read_phy = rtmdio_930x_read_phy,
.reset = rtmdio_930x_reset,
.setup_polling = rtmdio_930x_setup_polling,
.write_mmd_phy = rtmdio_930x_write_mmd_phy,
.write_phy = rtmdio_930x_write_phy,
};
static const struct rtmdio_config rtmdio_931x_cfg = {
.cpu_port = 56,
.raw_page = 8191,
.read_mmd_phy = rtmdio_931x_read_mmd_phy,
.read_phy = rtmdio_931x_read_phy,
.reset = rtmdio_931x_reset,
.setup_polling = rtmdio_931x_setup_polling,
.write_mmd_phy = rtmdio_931x_write_mmd_phy,
.write_phy = rtmdio_931x_write_phy,
};
static const struct of_device_id rtmdio_ids[] = {
{
.compatible = "realtek,rtl8380-mdio",
.data = &rtmdio_838x_cfg,
},
{
.compatible = "realtek,rtl8392-mdio",
.data = &rtmdio_839x_cfg,
},
{
.compatible = "realtek,rtl9301-mdio",
.data = &rtmdio_930x_cfg,
},
{
.compatible = "realtek,rtl9311-mdio",
.data = &rtmdio_931x_cfg,
},
{ /* sentinel */ }
};
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);
MODULE_DESCRIPTION("RTL83xx/RTL93xx MDIO driver");
MODULE_LICENSE("GPL");