162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the MDIO interface of Microsemi network switches. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Alexandre Belloni <alexandre.belloni@bootlin.com> 662306a36Sopenharmony_ci * Copyright (c) 2017 Microsemi Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/iopoll.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/mdio/mdio-mscc-miim.h> 1562306a36Sopenharmony_ci#include <linux/mfd/ocelot.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of_mdio.h> 1862306a36Sopenharmony_ci#include <linux/phy.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/property.h> 2162306a36Sopenharmony_ci#include <linux/regmap.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MSCC_MIIM_REG_STATUS 0x0 2462306a36Sopenharmony_ci#define MSCC_MIIM_STATUS_STAT_PENDING BIT(2) 2562306a36Sopenharmony_ci#define MSCC_MIIM_STATUS_STAT_BUSY BIT(3) 2662306a36Sopenharmony_ci#define MSCC_MIIM_REG_CMD 0x8 2762306a36Sopenharmony_ci#define MSCC_MIIM_CMD_OPR_WRITE BIT(1) 2862306a36Sopenharmony_ci#define MSCC_MIIM_CMD_OPR_READ BIT(2) 2962306a36Sopenharmony_ci#define MSCC_MIIM_CMD_WRDATA_SHIFT 4 3062306a36Sopenharmony_ci#define MSCC_MIIM_CMD_REGAD_SHIFT 20 3162306a36Sopenharmony_ci#define MSCC_MIIM_CMD_PHYAD_SHIFT 25 3262306a36Sopenharmony_ci#define MSCC_MIIM_CMD_VLD BIT(31) 3362306a36Sopenharmony_ci#define MSCC_MIIM_REG_DATA 0xC 3462306a36Sopenharmony_ci#define MSCC_MIIM_DATA_ERROR (BIT(16) | BIT(17)) 3562306a36Sopenharmony_ci#define MSCC_MIIM_REG_CFG 0x10 3662306a36Sopenharmony_ci#define MSCC_MIIM_CFG_PRESCALE_MASK GENMASK(7, 0) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MSCC_PHY_REG_PHY_CFG 0x0 3962306a36Sopenharmony_ci#define PHY_CFG_PHY_ENA (BIT(0) | BIT(1) | BIT(2) | BIT(3)) 4062306a36Sopenharmony_ci#define PHY_CFG_PHY_COMMON_RESET BIT(4) 4162306a36Sopenharmony_ci#define PHY_CFG_PHY_RESET (BIT(5) | BIT(6) | BIT(7) | BIT(8)) 4262306a36Sopenharmony_ci#define MSCC_PHY_REG_PHY_STATUS 0x4 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define LAN966X_CUPHY_COMMON_CFG 0x0 4562306a36Sopenharmony_ci#define CUPHY_COMMON_CFG_RESET_N BIT(0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct mscc_miim_info { 4862306a36Sopenharmony_ci unsigned int phy_reset_offset; 4962306a36Sopenharmony_ci unsigned int phy_reset_bits; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct mscc_miim_dev { 5362306a36Sopenharmony_ci struct regmap *regs; 5462306a36Sopenharmony_ci int mii_status_offset; 5562306a36Sopenharmony_ci bool ignore_read_errors; 5662306a36Sopenharmony_ci struct regmap *phy_regs; 5762306a36Sopenharmony_ci const struct mscc_miim_info *info; 5862306a36Sopenharmony_ci struct clk *clk; 5962306a36Sopenharmony_ci u32 bus_freq; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* When high resolution timers aren't built-in: we can't use usleep_range() as 6362306a36Sopenharmony_ci * we would sleep way too long. Use udelay() instead. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci#define mscc_readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us)\ 6662306a36Sopenharmony_ci({ \ 6762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) \ 6862306a36Sopenharmony_ci readx_poll_timeout_atomic(op, addr, val, cond, delay_us, \ 6962306a36Sopenharmony_ci timeout_us); \ 7062306a36Sopenharmony_ci readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us); \ 7162306a36Sopenharmony_ci}) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int mscc_miim_status(struct mii_bus *bus) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 7662306a36Sopenharmony_ci int val, ret; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ret = regmap_read(miim->regs, 7962306a36Sopenharmony_ci MSCC_MIIM_REG_STATUS + miim->mii_status_offset, &val); 8062306a36Sopenharmony_ci if (ret < 0) { 8162306a36Sopenharmony_ci WARN_ONCE(1, "mscc miim status read error %d\n", ret); 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return val; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int mscc_miim_wait_ready(struct mii_bus *bus) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 val; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return mscc_readx_poll_timeout(mscc_miim_status, bus, val, 9362306a36Sopenharmony_ci !(val & MSCC_MIIM_STATUS_STAT_BUSY), 50, 9462306a36Sopenharmony_ci 10000); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int mscc_miim_wait_pending(struct mii_bus *bus) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci u32 val; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return mscc_readx_poll_timeout(mscc_miim_status, bus, val, 10262306a36Sopenharmony_ci !(val & MSCC_MIIM_STATUS_STAT_PENDING), 10362306a36Sopenharmony_ci 50, 10000); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 10962306a36Sopenharmony_ci u32 val; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = mscc_miim_wait_pending(bus); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ret = regmap_write(miim->regs, 11762306a36Sopenharmony_ci MSCC_MIIM_REG_CMD + miim->mii_status_offset, 11862306a36Sopenharmony_ci MSCC_MIIM_CMD_VLD | 11962306a36Sopenharmony_ci (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | 12062306a36Sopenharmony_ci (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | 12162306a36Sopenharmony_ci MSCC_MIIM_CMD_OPR_READ); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (ret < 0) { 12462306a36Sopenharmony_ci WARN_ONCE(1, "mscc miim write cmd reg error %d\n", ret); 12562306a36Sopenharmony_ci goto out; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = mscc_miim_wait_ready(bus); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci goto out; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = regmap_read(miim->regs, 13362306a36Sopenharmony_ci MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val); 13462306a36Sopenharmony_ci if (ret < 0) { 13562306a36Sopenharmony_ci WARN_ONCE(1, "mscc miim read data reg error %d\n", ret); 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!miim->ignore_read_errors && !!(val & MSCC_MIIM_DATA_ERROR)) { 14062306a36Sopenharmony_ci ret = -EIO; 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = val & 0xFFFF; 14562306a36Sopenharmony_ciout: 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int mscc_miim_write(struct mii_bus *bus, int mii_id, 15062306a36Sopenharmony_ci int regnum, u16 value) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = mscc_miim_wait_pending(bus); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci goto out; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = regmap_write(miim->regs, 16062306a36Sopenharmony_ci MSCC_MIIM_REG_CMD + miim->mii_status_offset, 16162306a36Sopenharmony_ci MSCC_MIIM_CMD_VLD | 16262306a36Sopenharmony_ci (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | 16362306a36Sopenharmony_ci (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | 16462306a36Sopenharmony_ci (value << MSCC_MIIM_CMD_WRDATA_SHIFT) | 16562306a36Sopenharmony_ci MSCC_MIIM_CMD_OPR_WRITE); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (ret < 0) 16862306a36Sopenharmony_ci WARN_ONCE(1, "mscc miim write error %d\n", ret); 16962306a36Sopenharmony_ciout: 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int mscc_miim_reset(struct mii_bus *bus) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 17662306a36Sopenharmony_ci unsigned int offset, bits; 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!miim->phy_regs) 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci offset = miim->info->phy_reset_offset; 18362306a36Sopenharmony_ci bits = miim->info->phy_reset_bits; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = regmap_update_bits(miim->phy_regs, offset, bits, 0); 18662306a36Sopenharmony_ci if (ret < 0) { 18762306a36Sopenharmony_ci WARN_ONCE(1, "mscc reset set error %d\n", ret); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = regmap_update_bits(miim->phy_regs, offset, bits, bits); 19262306a36Sopenharmony_ci if (ret < 0) { 19362306a36Sopenharmony_ci WARN_ONCE(1, "mscc reset clear error %d\n", ret); 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mdelay(500); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct regmap_config mscc_miim_regmap_config = { 20362306a36Sopenharmony_ci .reg_bits = 32, 20462306a36Sopenharmony_ci .val_bits = 32, 20562306a36Sopenharmony_ci .reg_stride = 4, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic const struct regmap_config mscc_miim_phy_regmap_config = { 20962306a36Sopenharmony_ci .reg_bits = 32, 21062306a36Sopenharmony_ci .val_bits = 32, 21162306a36Sopenharmony_ci .reg_stride = 4, 21262306a36Sopenharmony_ci .name = "phy", 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciint mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name, 21662306a36Sopenharmony_ci struct regmap *mii_regmap, int status_offset, 21762306a36Sopenharmony_ci bool ignore_read_errors) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct mscc_miim_dev *miim; 22062306a36Sopenharmony_ci struct mii_bus *bus; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci bus = devm_mdiobus_alloc_size(dev, sizeof(*miim)); 22362306a36Sopenharmony_ci if (!bus) 22462306a36Sopenharmony_ci return -ENOMEM; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci bus->name = name; 22762306a36Sopenharmony_ci bus->read = mscc_miim_read; 22862306a36Sopenharmony_ci bus->write = mscc_miim_write; 22962306a36Sopenharmony_ci bus->reset = mscc_miim_reset; 23062306a36Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); 23162306a36Sopenharmony_ci bus->parent = dev; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci miim = bus->priv; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci *pbus = bus; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci miim->regs = mii_regmap; 23862306a36Sopenharmony_ci miim->mii_status_offset = status_offset; 23962306a36Sopenharmony_ci miim->ignore_read_errors = ignore_read_errors; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci *pbus = bus; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ciEXPORT_SYMBOL(mscc_miim_setup); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int mscc_miim_clk_set(struct mii_bus *bus) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 25062306a36Sopenharmony_ci unsigned long rate; 25162306a36Sopenharmony_ci u32 div; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Keep the current settings */ 25462306a36Sopenharmony_ci if (!miim->bus_freq) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci rate = clk_get_rate(miim->clk); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci div = DIV_ROUND_UP(rate, 2 * miim->bus_freq) - 1; 26062306a36Sopenharmony_ci if (div == 0 || div & ~MSCC_MIIM_CFG_PRESCALE_MASK) { 26162306a36Sopenharmony_ci dev_err(&bus->dev, "Incorrect MDIO clock frequency\n"); 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return regmap_update_bits(miim->regs, MSCC_MIIM_REG_CFG, 26662306a36Sopenharmony_ci MSCC_MIIM_CFG_PRESCALE_MASK, div); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int mscc_miim_probe(struct platform_device *pdev) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 27262306a36Sopenharmony_ci struct regmap *mii_regmap, *phy_regmap; 27362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 27462306a36Sopenharmony_ci struct mscc_miim_dev *miim; 27562306a36Sopenharmony_ci struct mii_bus *bus; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci mii_regmap = ocelot_regmap_from_resource(pdev, 0, 27962306a36Sopenharmony_ci &mscc_miim_regmap_config); 28062306a36Sopenharmony_ci if (IS_ERR(mii_regmap)) 28162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mii_regmap), 28262306a36Sopenharmony_ci "Unable to create MIIM regmap\n"); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* This resource is optional */ 28562306a36Sopenharmony_ci phy_regmap = ocelot_regmap_from_resource_optional(pdev, 1, 28662306a36Sopenharmony_ci &mscc_miim_phy_regmap_config); 28762306a36Sopenharmony_ci if (IS_ERR(phy_regmap)) 28862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(phy_regmap), 28962306a36Sopenharmony_ci "Unable to create phy register regmap\n"); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = mscc_miim_setup(dev, &bus, "mscc_miim", mii_regmap, 0, false); 29262306a36Sopenharmony_ci if (ret < 0) { 29362306a36Sopenharmony_ci dev_err(dev, "Unable to setup the MDIO bus\n"); 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci miim = bus->priv; 29862306a36Sopenharmony_ci miim->phy_regs = phy_regmap; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci miim->info = device_get_match_data(dev); 30162306a36Sopenharmony_ci if (!miim->info) 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci miim->clk = devm_clk_get_optional(dev, NULL); 30562306a36Sopenharmony_ci if (IS_ERR(miim->clk)) 30662306a36Sopenharmony_ci return PTR_ERR(miim->clk); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci of_property_read_u32(np, "clock-frequency", &miim->bus_freq); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (miim->bus_freq && !miim->clk) { 31162306a36Sopenharmony_ci dev_err(dev, "cannot use clock-frequency without a clock\n"); 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = clk_prepare_enable(miim->clk); 31662306a36Sopenharmony_ci if (ret) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ret = mscc_miim_clk_set(bus); 32062306a36Sopenharmony_ci if (ret) 32162306a36Sopenharmony_ci goto out_disable_clk; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = of_mdiobus_register(bus, np); 32462306a36Sopenharmony_ci if (ret < 0) { 32562306a36Sopenharmony_ci dev_err(dev, "Cannot register MDIO bus (%d)\n", ret); 32662306a36Sopenharmony_ci goto out_disable_clk; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci platform_set_drvdata(pdev, bus); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciout_disable_clk: 33462306a36Sopenharmony_ci clk_disable_unprepare(miim->clk); 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int mscc_miim_remove(struct platform_device *pdev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct mii_bus *bus = platform_get_drvdata(pdev); 34162306a36Sopenharmony_ci struct mscc_miim_dev *miim = bus->priv; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci clk_disable_unprepare(miim->clk); 34462306a36Sopenharmony_ci mdiobus_unregister(bus); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic const struct mscc_miim_info mscc_ocelot_miim_info = { 35062306a36Sopenharmony_ci .phy_reset_offset = MSCC_PHY_REG_PHY_CFG, 35162306a36Sopenharmony_ci .phy_reset_bits = PHY_CFG_PHY_ENA | PHY_CFG_PHY_COMMON_RESET | 35262306a36Sopenharmony_ci PHY_CFG_PHY_RESET, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const struct mscc_miim_info microchip_lan966x_miim_info = { 35662306a36Sopenharmony_ci .phy_reset_offset = LAN966X_CUPHY_COMMON_CFG, 35762306a36Sopenharmony_ci .phy_reset_bits = CUPHY_COMMON_CFG_RESET_N, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic const struct of_device_id mscc_miim_match[] = { 36162306a36Sopenharmony_ci { 36262306a36Sopenharmony_ci .compatible = "mscc,ocelot-miim", 36362306a36Sopenharmony_ci .data = &mscc_ocelot_miim_info 36462306a36Sopenharmony_ci }, { 36562306a36Sopenharmony_ci .compatible = "microchip,lan966x-miim", 36662306a36Sopenharmony_ci .data = µchip_lan966x_miim_info 36762306a36Sopenharmony_ci }, 36862306a36Sopenharmony_ci { } 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mscc_miim_match); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic struct platform_driver mscc_miim_driver = { 37362306a36Sopenharmony_ci .probe = mscc_miim_probe, 37462306a36Sopenharmony_ci .remove = mscc_miim_remove, 37562306a36Sopenharmony_ci .driver = { 37662306a36Sopenharmony_ci .name = "mscc-miim", 37762306a36Sopenharmony_ci .of_match_table = mscc_miim_match, 37862306a36Sopenharmony_ci }, 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cimodule_platform_driver(mscc_miim_driver); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsemi MIIM driver"); 38462306a36Sopenharmony_ciMODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); 38562306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 386