162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014-2015 Hisilicon Limited. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/acpi.h> 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_mdio.h> 1862306a36Sopenharmony_ci#include <linux/of_platform.h> 1962306a36Sopenharmony_ci#include <linux/phy.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/regmap.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MDIO_DRV_NAME "Hi-HNS_MDIO" 2462306a36Sopenharmony_ci#define MDIO_BUS_NAME "Hisilicon MII Bus" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MDIO_TIMEOUT 1000000 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct hns_mdio_sc_reg { 2962306a36Sopenharmony_ci u16 mdio_clk_en; 3062306a36Sopenharmony_ci u16 mdio_clk_dis; 3162306a36Sopenharmony_ci u16 mdio_reset_req; 3262306a36Sopenharmony_ci u16 mdio_reset_dreq; 3362306a36Sopenharmony_ci u16 mdio_clk_st; 3462306a36Sopenharmony_ci u16 mdio_reset_st; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct hns_mdio_device { 3862306a36Sopenharmony_ci u8 __iomem *vbase; /* mdio reg base address */ 3962306a36Sopenharmony_ci struct regmap *subctrl_vbase; 4062306a36Sopenharmony_ci struct hns_mdio_sc_reg sc_reg; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* mdio reg */ 4462306a36Sopenharmony_ci#define MDIO_COMMAND_REG 0x0 4562306a36Sopenharmony_ci#define MDIO_ADDR_REG 0x4 4662306a36Sopenharmony_ci#define MDIO_WDATA_REG 0x8 4762306a36Sopenharmony_ci#define MDIO_RDATA_REG 0xc 4862306a36Sopenharmony_ci#define MDIO_STA_REG 0x10 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* cfg phy bit map */ 5162306a36Sopenharmony_ci#define MDIO_CMD_DEVAD_M 0x1f 5262306a36Sopenharmony_ci#define MDIO_CMD_DEVAD_S 0 5362306a36Sopenharmony_ci#define MDIO_CMD_PRTAD_M 0x1f 5462306a36Sopenharmony_ci#define MDIO_CMD_PRTAD_S 5 5562306a36Sopenharmony_ci#define MDIO_CMD_OP_S 10 5662306a36Sopenharmony_ci#define MDIO_CMD_ST_S 12 5762306a36Sopenharmony_ci#define MDIO_CMD_START_B 14 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define MDIO_ADDR_DATA_M 0xffff 6062306a36Sopenharmony_ci#define MDIO_ADDR_DATA_S 0 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define MDIO_WDATA_DATA_M 0xffff 6362306a36Sopenharmony_ci#define MDIO_WDATA_DATA_S 0 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define MDIO_RDATA_DATA_M 0xffff 6662306a36Sopenharmony_ci#define MDIO_RDATA_DATA_S 0 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MDIO_STATE_STA_B 0 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum mdio_st_clause { 7162306a36Sopenharmony_ci MDIO_ST_CLAUSE_45 = 0, 7262306a36Sopenharmony_ci MDIO_ST_CLAUSE_22 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cienum mdio_c22_op_seq { 7662306a36Sopenharmony_ci MDIO_C22_WRITE = 1, 7762306a36Sopenharmony_ci MDIO_C22_READ = 2 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cienum mdio_c45_op_seq { 8162306a36Sopenharmony_ci MDIO_C45_WRITE_ADDR = 0, 8262306a36Sopenharmony_ci MDIO_C45_WRITE_DATA, 8362306a36Sopenharmony_ci MDIO_C45_READ_INCREMENT, 8462306a36Sopenharmony_ci MDIO_C45_READ 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* peri subctrl reg */ 8862306a36Sopenharmony_ci#define MDIO_SC_CLK_EN 0x338 8962306a36Sopenharmony_ci#define MDIO_SC_CLK_DIS 0x33C 9062306a36Sopenharmony_ci#define MDIO_SC_RESET_REQ 0xA38 9162306a36Sopenharmony_ci#define MDIO_SC_RESET_DREQ 0xA3C 9262306a36Sopenharmony_ci#define MDIO_SC_CLK_ST 0x531C 9362306a36Sopenharmony_ci#define MDIO_SC_RESET_ST 0x5A1C 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void mdio_write_reg(u8 __iomem *base, u32 reg, u32 value) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci writel_relaxed(value, base + reg); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define MDIO_WRITE_REG(a, reg, value) \ 10162306a36Sopenharmony_ci mdio_write_reg((a)->vbase, (reg), (value)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic u32 mdio_read_reg(u8 __iomem *base, u32 reg) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return readl_relaxed(base + reg); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define mdio_set_field(origin, mask, shift, val) \ 10962306a36Sopenharmony_ci do { \ 11062306a36Sopenharmony_ci (origin) &= (~((mask) << (shift))); \ 11162306a36Sopenharmony_ci (origin) |= (((val) & (mask)) << (shift)); \ 11262306a36Sopenharmony_ci } while (0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void mdio_set_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift, 11762306a36Sopenharmony_ci u32 val) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 origin = mdio_read_reg(base, reg); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci mdio_set_field(origin, mask, shift, val); 12262306a36Sopenharmony_ci mdio_write_reg(base, reg, origin); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \ 12662306a36Sopenharmony_ci mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic u32 mdio_get_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci u32 origin; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci origin = mdio_read_reg(base, reg); 13362306a36Sopenharmony_ci return mdio_get_field(origin, mask, shift); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \ 13762306a36Sopenharmony_ci mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift)) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define MDIO_GET_REG_BIT(dev, reg, bit) \ 14062306a36Sopenharmony_ci mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit)) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define MDIO_CHECK_SET_ST 1 14362306a36Sopenharmony_ci#define MDIO_CHECK_CLR_ST 0 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, 14662306a36Sopenharmony_ci u32 cfg_reg, u32 set_val, 14762306a36Sopenharmony_ci u32 st_reg, u32 st_msk, u8 check_st) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci u32 time_cnt; 15062306a36Sopenharmony_ci u32 reg_value; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci regmap_write(mdio_dev->subctrl_vbase, cfg_reg, set_val); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { 15662306a36Sopenharmony_ci ret = regmap_read(mdio_dev->subctrl_vbase, st_reg, ®_value); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci reg_value &= st_msk; 16162306a36Sopenharmony_ci if ((!!check_st) == (!!reg_value)) 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if ((!!check_st) != (!!reg_value)) 16662306a36Sopenharmony_ci return -EBUSY; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int hns_mdio_wait_ready(struct mii_bus *bus) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 17462306a36Sopenharmony_ci u32 cmd_reg_value; 17562306a36Sopenharmony_ci int i; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* waiting for MDIO_COMMAND_REG's mdio_start==0 */ 17862306a36Sopenharmony_ci /* after that can do read or write*/ 17962306a36Sopenharmony_ci for (i = 0; i < MDIO_TIMEOUT; i++) { 18062306a36Sopenharmony_ci cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, 18162306a36Sopenharmony_ci MDIO_COMMAND_REG, 18262306a36Sopenharmony_ci MDIO_CMD_START_B); 18362306a36Sopenharmony_ci if (!cmd_reg_value) 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci if ((i == MDIO_TIMEOUT) && cmd_reg_value) 18762306a36Sopenharmony_ci return -ETIMEDOUT; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, 19362306a36Sopenharmony_ci u8 is_c45, u8 op, u8 phy_id, u16 cmd) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 cmd_reg_value; 19662306a36Sopenharmony_ci u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci cmd_reg_value = st << MDIO_CMD_ST_S; 19962306a36Sopenharmony_ci cmd_reg_value |= op << MDIO_CMD_OP_S; 20062306a36Sopenharmony_ci cmd_reg_value |= 20162306a36Sopenharmony_ci (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S; 20262306a36Sopenharmony_ci cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S; 20362306a36Sopenharmony_ci cmd_reg_value |= 1 << MDIO_CMD_START_B; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/** 20962306a36Sopenharmony_ci * hns_mdio_write_c22 - access phy register 21062306a36Sopenharmony_ci * @bus: mdio bus 21162306a36Sopenharmony_ci * @phy_id: phy id 21262306a36Sopenharmony_ci * @regnum: register num 21362306a36Sopenharmony_ci * @data: register value 21462306a36Sopenharmony_ci * 21562306a36Sopenharmony_ci * Return 0 on success, negative on failure 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic int hns_mdio_write_c22(struct mii_bus *bus, 21862306a36Sopenharmony_ci int phy_id, int regnum, u16 data) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 22162306a36Sopenharmony_ci u16 reg = (u16)(regnum & 0xffff); 22262306a36Sopenharmony_ci u16 cmd_reg_cfg; 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci u8 op; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci dev_dbg(&bus->dev, "mdio write %s,base is %p\n", 22762306a36Sopenharmony_ci bus->id, mdio_dev->vbase); 22862306a36Sopenharmony_ci dev_dbg(&bus->dev, "phy id=%d, reg=%#x, write data=%d\n", 22962306a36Sopenharmony_ci phy_id, reg, data); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* wait for ready */ 23262306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 23362306a36Sopenharmony_ci if (ret) { 23462306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci cmd_reg_cfg = reg; 23962306a36Sopenharmony_ci op = MDIO_C22_WRITE; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, 24262306a36Sopenharmony_ci MDIO_WDATA_DATA_S, data); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, false, op, phy_id, cmd_reg_cfg); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * hns_mdio_write_c45 - access phy register 25162306a36Sopenharmony_ci * @bus: mdio bus 25262306a36Sopenharmony_ci * @phy_id: phy id 25362306a36Sopenharmony_ci * @devad: device address to read 25462306a36Sopenharmony_ci * @regnum: register num 25562306a36Sopenharmony_ci * @data: register value 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Return 0 on success, negative on failure 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic int hns_mdio_write_c45(struct mii_bus *bus, int phy_id, int devad, 26062306a36Sopenharmony_ci int regnum, u16 data) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 26362306a36Sopenharmony_ci u16 reg = (u16)(regnum & 0xffff); 26462306a36Sopenharmony_ci u16 cmd_reg_cfg; 26562306a36Sopenharmony_ci int ret; 26662306a36Sopenharmony_ci u8 op; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci dev_dbg(&bus->dev, "mdio write %s,base is %p\n", 26962306a36Sopenharmony_ci bus->id, mdio_dev->vbase); 27062306a36Sopenharmony_ci dev_dbg(&bus->dev, "phy id=%d, devad=%d, reg=%#x, write data=%d\n", 27162306a36Sopenharmony_ci phy_id, devad, reg, data); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* wait for ready */ 27462306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 27562306a36Sopenharmony_ci if (ret) { 27662306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* config the cmd-reg to write addr*/ 28162306a36Sopenharmony_ci MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, 28262306a36Sopenharmony_ci MDIO_ADDR_DATA_S, reg); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_WRITE_ADDR, phy_id, devad); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* check for read or write opt is finished */ 28762306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 28862306a36Sopenharmony_ci if (ret) { 28962306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* config the data needed writing */ 29462306a36Sopenharmony_ci cmd_reg_cfg = devad; 29562306a36Sopenharmony_ci op = MDIO_C45_WRITE_DATA; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, 29862306a36Sopenharmony_ci MDIO_WDATA_DATA_S, data); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, true, op, phy_id, cmd_reg_cfg); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * hns_mdio_read_c22 - access phy register 30762306a36Sopenharmony_ci * @bus: mdio bus 30862306a36Sopenharmony_ci * @phy_id: phy id 30962306a36Sopenharmony_ci * @regnum: register num 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Return phy register value 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic int hns_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 31662306a36Sopenharmony_ci u16 reg = (u16)(regnum & 0xffff); 31762306a36Sopenharmony_ci u16 reg_val; 31862306a36Sopenharmony_ci int ret; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dev_dbg(&bus->dev, "mdio read %s,base is %p\n", 32162306a36Sopenharmony_ci bus->id, mdio_dev->vbase); 32262306a36Sopenharmony_ci dev_dbg(&bus->dev, "phy id=%d, reg=%#x!\n", phy_id, reg); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Step 1: wait for ready */ 32562306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 32662306a36Sopenharmony_ci if (ret) { 32762306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, false, MDIO_C22_READ, phy_id, reg); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Step 2: waiting for MDIO_COMMAND_REG 's mdio_start==0,*/ 33462306a36Sopenharmony_ci /* check for read or write opt is finished */ 33562306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 33662306a36Sopenharmony_ci if (ret) { 33762306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); 34262306a36Sopenharmony_ci if (reg_val) { 34362306a36Sopenharmony_ci dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); 34462306a36Sopenharmony_ci return -EBUSY; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Step 3; get out data*/ 34862306a36Sopenharmony_ci reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, 34962306a36Sopenharmony_ci MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return reg_val; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * hns_mdio_read_c45 - access phy register 35662306a36Sopenharmony_ci * @bus: mdio bus 35762306a36Sopenharmony_ci * @phy_id: phy id 35862306a36Sopenharmony_ci * @devad: device address to read 35962306a36Sopenharmony_ci * @regnum: register num 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Return phy register value 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistatic int hns_mdio_read_c45(struct mii_bus *bus, int phy_id, int devad, 36462306a36Sopenharmony_ci int regnum) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 36762306a36Sopenharmony_ci u16 reg = (u16)(regnum & 0xffff); 36862306a36Sopenharmony_ci u16 reg_val; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci dev_dbg(&bus->dev, "mdio read %s,base is %p\n", 37262306a36Sopenharmony_ci bus->id, mdio_dev->vbase); 37362306a36Sopenharmony_ci dev_dbg(&bus->dev, "phy id=%d, devad=%d, reg=%#x!\n", 37462306a36Sopenharmony_ci phy_id, devad, reg); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Step 1: wait for ready */ 37762306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 37862306a36Sopenharmony_ci if (ret) { 37962306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 38062306a36Sopenharmony_ci return ret; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, 38462306a36Sopenharmony_ci MDIO_ADDR_DATA_S, reg); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Step 2; config the cmd-reg to write addr*/ 38762306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_WRITE_ADDR, phy_id, devad); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Step 3: check for read or write opt is finished */ 39062306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 39162306a36Sopenharmony_ci if (ret) { 39262306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_READ, phy_id, devad); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Step 5: waiting for MDIO_COMMAND_REG 's mdio_start==0,*/ 39962306a36Sopenharmony_ci /* check for read or write opt is finished */ 40062306a36Sopenharmony_ci ret = hns_mdio_wait_ready(bus); 40162306a36Sopenharmony_ci if (ret) { 40262306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO bus is busy\n"); 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); 40762306a36Sopenharmony_ci if (reg_val) { 40862306a36Sopenharmony_ci dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); 40962306a36Sopenharmony_ci return -EBUSY; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Step 6; get out data*/ 41362306a36Sopenharmony_ci reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, 41462306a36Sopenharmony_ci MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return reg_val; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * hns_mdio_reset - reset mdio bus 42162306a36Sopenharmony_ci * @bus: mdio bus 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Return 0 on success, negative on failure 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic int hns_mdio_reset(struct mii_bus *bus) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev = bus->priv; 42862306a36Sopenharmony_ci const struct hns_mdio_sc_reg *sc_reg; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (dev_of_node(bus->parent)) { 43262306a36Sopenharmony_ci if (!mdio_dev->subctrl_vbase) { 43362306a36Sopenharmony_ci dev_err(&bus->dev, "mdio sys ctl reg has not mapped\n"); 43462306a36Sopenharmony_ci return -ENODEV; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci sc_reg = &mdio_dev->sc_reg; 43862306a36Sopenharmony_ci /* 1. reset req, and read reset st check */ 43962306a36Sopenharmony_ci ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_reset_req, 44062306a36Sopenharmony_ci 0x1, sc_reg->mdio_reset_st, 0x1, 44162306a36Sopenharmony_ci MDIO_CHECK_SET_ST); 44262306a36Sopenharmony_ci if (ret) { 44362306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO reset fail\n"); 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 2. dis clk, and read clk st check */ 44862306a36Sopenharmony_ci ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_clk_dis, 44962306a36Sopenharmony_ci 0x1, sc_reg->mdio_clk_st, 0x1, 45062306a36Sopenharmony_ci MDIO_CHECK_CLR_ST); 45162306a36Sopenharmony_ci if (ret) { 45262306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO dis clk fail\n"); 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 3. reset dreq, and read reset st check */ 45762306a36Sopenharmony_ci ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_reset_dreq, 45862306a36Sopenharmony_ci 0x1, sc_reg->mdio_reset_st, 0x1, 45962306a36Sopenharmony_ci MDIO_CHECK_CLR_ST); 46062306a36Sopenharmony_ci if (ret) { 46162306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO dis clk fail\n"); 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 4. en clk, and read clk st check */ 46662306a36Sopenharmony_ci ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_clk_en, 46762306a36Sopenharmony_ci 0x1, sc_reg->mdio_clk_st, 0x1, 46862306a36Sopenharmony_ci MDIO_CHECK_SET_ST); 46962306a36Sopenharmony_ci if (ret) 47062306a36Sopenharmony_ci dev_err(&bus->dev, "MDIO en clk fail\n"); 47162306a36Sopenharmony_ci } else if (is_acpi_node(bus->parent->fwnode)) { 47262306a36Sopenharmony_ci acpi_status s; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci s = acpi_evaluate_object(ACPI_HANDLE(bus->parent), 47562306a36Sopenharmony_ci "_RST", NULL, NULL); 47662306a36Sopenharmony_ci if (ACPI_FAILURE(s)) { 47762306a36Sopenharmony_ci dev_err(&bus->dev, "Reset failed, return:%#x\n", s); 47862306a36Sopenharmony_ci ret = -EBUSY; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci ret = 0; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } else { 48362306a36Sopenharmony_ci dev_err(&bus->dev, "Can not get cfg data from DT or ACPI\n"); 48462306a36Sopenharmony_ci ret = -ENXIO; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci/** 49062306a36Sopenharmony_ci * hns_mdio_probe - probe mdio device 49162306a36Sopenharmony_ci * @pdev: mdio platform device 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * Return 0 on success, negative on failure 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistatic int hns_mdio_probe(struct platform_device *pdev) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct hns_mdio_device *mdio_dev; 49862306a36Sopenharmony_ci struct mii_bus *new_bus; 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!pdev) { 50262306a36Sopenharmony_ci dev_err(NULL, "pdev is NULL!\r\n"); 50362306a36Sopenharmony_ci return -ENODEV; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL); 50762306a36Sopenharmony_ci if (!mdio_dev) 50862306a36Sopenharmony_ci return -ENOMEM; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci new_bus = devm_mdiobus_alloc(&pdev->dev); 51162306a36Sopenharmony_ci if (!new_bus) { 51262306a36Sopenharmony_ci dev_err(&pdev->dev, "mdiobus_alloc fail!\n"); 51362306a36Sopenharmony_ci return -ENOMEM; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci new_bus->name = MDIO_BUS_NAME; 51762306a36Sopenharmony_ci new_bus->read = hns_mdio_read_c22; 51862306a36Sopenharmony_ci new_bus->write = hns_mdio_write_c22; 51962306a36Sopenharmony_ci new_bus->read_c45 = hns_mdio_read_c45; 52062306a36Sopenharmony_ci new_bus->write_c45 = hns_mdio_write_c45; 52162306a36Sopenharmony_ci new_bus->reset = hns_mdio_reset; 52262306a36Sopenharmony_ci new_bus->priv = mdio_dev; 52362306a36Sopenharmony_ci new_bus->parent = &pdev->dev; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mdio_dev->vbase = devm_platform_ioremap_resource(pdev, 0); 52662306a36Sopenharmony_ci if (IS_ERR(mdio_dev->vbase)) { 52762306a36Sopenharmony_ci ret = PTR_ERR(mdio_dev->vbase); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci platform_set_drvdata(pdev, new_bus); 53262306a36Sopenharmony_ci snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%s", "Mii", 53362306a36Sopenharmony_ci dev_name(&pdev->dev)); 53462306a36Sopenharmony_ci if (dev_of_node(&pdev->dev)) { 53562306a36Sopenharmony_ci struct of_phandle_args reg_args; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, 53862306a36Sopenharmony_ci "subctrl-vbase", 53962306a36Sopenharmony_ci 4, 54062306a36Sopenharmony_ci 0, 54162306a36Sopenharmony_ci ®_args); 54262306a36Sopenharmony_ci if (!ret) { 54362306a36Sopenharmony_ci mdio_dev->subctrl_vbase = 54462306a36Sopenharmony_ci syscon_node_to_regmap(reg_args.np); 54562306a36Sopenharmony_ci if (IS_ERR(mdio_dev->subctrl_vbase)) { 54662306a36Sopenharmony_ci dev_warn(&pdev->dev, "syscon_node_to_regmap error\n"); 54762306a36Sopenharmony_ci mdio_dev->subctrl_vbase = NULL; 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci if (reg_args.args_count == 4) { 55062306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_en = 55162306a36Sopenharmony_ci (u16)reg_args.args[0]; 55262306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_dis = 55362306a36Sopenharmony_ci (u16)reg_args.args[0] + 4; 55462306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_req = 55562306a36Sopenharmony_ci (u16)reg_args.args[1]; 55662306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_dreq = 55762306a36Sopenharmony_ci (u16)reg_args.args[1] + 4; 55862306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_st = 55962306a36Sopenharmony_ci (u16)reg_args.args[2]; 56062306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_st = 56162306a36Sopenharmony_ci (u16)reg_args.args[3]; 56262306a36Sopenharmony_ci } else { 56362306a36Sopenharmony_ci /* for compatible */ 56462306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_en = 56562306a36Sopenharmony_ci MDIO_SC_CLK_EN; 56662306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_dis = 56762306a36Sopenharmony_ci MDIO_SC_CLK_DIS; 56862306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_req = 56962306a36Sopenharmony_ci MDIO_SC_RESET_REQ; 57062306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_dreq = 57162306a36Sopenharmony_ci MDIO_SC_RESET_DREQ; 57262306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_clk_st = 57362306a36Sopenharmony_ci MDIO_SC_CLK_ST; 57462306a36Sopenharmony_ci mdio_dev->sc_reg.mdio_reset_st = 57562306a36Sopenharmony_ci MDIO_SC_RESET_ST; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } else { 57962306a36Sopenharmony_ci dev_warn(&pdev->dev, "find syscon ret = %#x\n", ret); 58062306a36Sopenharmony_ci mdio_dev->subctrl_vbase = NULL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = of_mdiobus_register(new_bus, pdev->dev.of_node); 58462306a36Sopenharmony_ci } else if (is_acpi_node(pdev->dev.fwnode)) { 58562306a36Sopenharmony_ci /* Clear all the IRQ properties */ 58662306a36Sopenharmony_ci memset(new_bus->irq, PHY_POLL, 4 * PHY_MAX_ADDR); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Mask out all PHYs from auto probing. */ 58962306a36Sopenharmony_ci new_bus->phy_mask = ~0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Register the MDIO bus */ 59262306a36Sopenharmony_ci ret = mdiobus_register(new_bus); 59362306a36Sopenharmony_ci } else { 59462306a36Sopenharmony_ci dev_err(&pdev->dev, "Can not get cfg data from DT or ACPI\n"); 59562306a36Sopenharmony_ci ret = -ENXIO; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (ret) { 59962306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register as MDIO bus!\n"); 60062306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/** 60862306a36Sopenharmony_ci * hns_mdio_remove - remove mdio device 60962306a36Sopenharmony_ci * @pdev: mdio platform device 61062306a36Sopenharmony_ci * 61162306a36Sopenharmony_ci * Return 0 on success, negative on failure 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_cistatic int hns_mdio_remove(struct platform_device *pdev) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct mii_bus *bus; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci bus = platform_get_drvdata(pdev); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mdiobus_unregister(bus); 62062306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic const struct of_device_id hns_mdio_match[] = { 62562306a36Sopenharmony_ci {.compatible = "hisilicon,mdio"}, 62662306a36Sopenharmony_ci {.compatible = "hisilicon,hns-mdio"}, 62762306a36Sopenharmony_ci {} 62862306a36Sopenharmony_ci}; 62962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hns_mdio_match); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic const struct acpi_device_id hns_mdio_acpi_match[] = { 63262306a36Sopenharmony_ci { "HISI0141", 0 }, 63362306a36Sopenharmony_ci { }, 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hns_mdio_acpi_match); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic struct platform_driver hns_mdio_driver = { 63862306a36Sopenharmony_ci .probe = hns_mdio_probe, 63962306a36Sopenharmony_ci .remove = hns_mdio_remove, 64062306a36Sopenharmony_ci .driver = { 64162306a36Sopenharmony_ci .name = MDIO_DRV_NAME, 64262306a36Sopenharmony_ci .of_match_table = hns_mdio_match, 64362306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(hns_mdio_acpi_match), 64462306a36Sopenharmony_ci }, 64562306a36Sopenharmony_ci}; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cimodule_platform_driver(hns_mdio_driver); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 65062306a36Sopenharmony_ciMODULE_AUTHOR("Huawei Tech. Co., Ltd."); 65162306a36Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon HNS MDIO driver"); 65262306a36Sopenharmony_ciMODULE_ALIAS("platform:" MDIO_DRV_NAME); 653