18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Hisilicon Limited.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/acpi.h>
78c2ecf20Sopenharmony_ci#include <linux/errno.h>
88c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
188c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
198c2ecf20Sopenharmony_ci#include <linux/phy.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/regmap.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define MDIO_DRV_NAME "Hi-HNS_MDIO"
248c2ecf20Sopenharmony_ci#define MDIO_BUS_NAME "Hisilicon MII Bus"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define MDIO_TIMEOUT			1000000
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct hns_mdio_sc_reg {
298c2ecf20Sopenharmony_ci	u16 mdio_clk_en;
308c2ecf20Sopenharmony_ci	u16 mdio_clk_dis;
318c2ecf20Sopenharmony_ci	u16 mdio_reset_req;
328c2ecf20Sopenharmony_ci	u16 mdio_reset_dreq;
338c2ecf20Sopenharmony_ci	u16 mdio_clk_st;
348c2ecf20Sopenharmony_ci	u16 mdio_reset_st;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct hns_mdio_device {
388c2ecf20Sopenharmony_ci	u8 __iomem *vbase;		/* mdio reg base address */
398c2ecf20Sopenharmony_ci	struct regmap *subctrl_vbase;
408c2ecf20Sopenharmony_ci	struct hns_mdio_sc_reg sc_reg;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* mdio reg */
448c2ecf20Sopenharmony_ci#define MDIO_COMMAND_REG		0x0
458c2ecf20Sopenharmony_ci#define MDIO_ADDR_REG			0x4
468c2ecf20Sopenharmony_ci#define MDIO_WDATA_REG			0x8
478c2ecf20Sopenharmony_ci#define MDIO_RDATA_REG			0xc
488c2ecf20Sopenharmony_ci#define MDIO_STA_REG			0x10
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* cfg phy bit map */
518c2ecf20Sopenharmony_ci#define MDIO_CMD_DEVAD_M	0x1f
528c2ecf20Sopenharmony_ci#define MDIO_CMD_DEVAD_S	0
538c2ecf20Sopenharmony_ci#define MDIO_CMD_PRTAD_M	0x1f
548c2ecf20Sopenharmony_ci#define MDIO_CMD_PRTAD_S	5
558c2ecf20Sopenharmony_ci#define MDIO_CMD_OP_S		10
568c2ecf20Sopenharmony_ci#define MDIO_CMD_ST_S		12
578c2ecf20Sopenharmony_ci#define MDIO_CMD_START_B	14
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define MDIO_ADDR_DATA_M	0xffff
608c2ecf20Sopenharmony_ci#define MDIO_ADDR_DATA_S	0
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define MDIO_WDATA_DATA_M	0xffff
638c2ecf20Sopenharmony_ci#define MDIO_WDATA_DATA_S	0
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define MDIO_RDATA_DATA_M	0xffff
668c2ecf20Sopenharmony_ci#define MDIO_RDATA_DATA_S	0
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define MDIO_STATE_STA_B	0
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cienum mdio_st_clause {
718c2ecf20Sopenharmony_ci	MDIO_ST_CLAUSE_45 = 0,
728c2ecf20Sopenharmony_ci	MDIO_ST_CLAUSE_22
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cienum mdio_c22_op_seq {
768c2ecf20Sopenharmony_ci	MDIO_C22_WRITE = 1,
778c2ecf20Sopenharmony_ci	MDIO_C22_READ = 2
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cienum mdio_c45_op_seq {
818c2ecf20Sopenharmony_ci	MDIO_C45_WRITE_ADDR = 0,
828c2ecf20Sopenharmony_ci	MDIO_C45_WRITE_DATA,
838c2ecf20Sopenharmony_ci	MDIO_C45_READ_INCREMENT,
848c2ecf20Sopenharmony_ci	MDIO_C45_READ
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* peri subctrl reg */
888c2ecf20Sopenharmony_ci#define MDIO_SC_CLK_EN		0x338
898c2ecf20Sopenharmony_ci#define MDIO_SC_CLK_DIS		0x33C
908c2ecf20Sopenharmony_ci#define MDIO_SC_RESET_REQ	0xA38
918c2ecf20Sopenharmony_ci#define MDIO_SC_RESET_DREQ	0xA3C
928c2ecf20Sopenharmony_ci#define MDIO_SC_CLK_ST		0x531C
938c2ecf20Sopenharmony_ci#define MDIO_SC_RESET_ST	0x5A1C
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void mdio_write_reg(u8 __iomem *base, u32 reg, u32 value)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	writel_relaxed(value, base + reg);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define MDIO_WRITE_REG(a, reg, value) \
1018c2ecf20Sopenharmony_ci	mdio_write_reg((a)->vbase, (reg), (value))
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic u32 mdio_read_reg(u8 __iomem *base, u32 reg)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	return readl_relaxed(base + reg);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define mdio_set_field(origin, mask, shift, val) \
1098c2ecf20Sopenharmony_ci	do { \
1108c2ecf20Sopenharmony_ci		(origin) &= (~((mask) << (shift))); \
1118c2ecf20Sopenharmony_ci		(origin) |= (((val) & (mask)) << (shift)); \
1128c2ecf20Sopenharmony_ci	} while (0)
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask))
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void mdio_set_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift,
1178c2ecf20Sopenharmony_ci			       u32 val)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u32 origin = mdio_read_reg(base, reg);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	mdio_set_field(origin, mask, shift, val);
1228c2ecf20Sopenharmony_ci	mdio_write_reg(base, reg, origin);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \
1268c2ecf20Sopenharmony_ci	mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val))
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic u32 mdio_get_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	u32 origin;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	origin = mdio_read_reg(base, reg);
1338c2ecf20Sopenharmony_ci	return mdio_get_field(origin, mask, shift);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \
1378c2ecf20Sopenharmony_ci		mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift))
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define MDIO_GET_REG_BIT(dev, reg, bit) \
1408c2ecf20Sopenharmony_ci		mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit))
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define MDIO_CHECK_SET_ST	1
1438c2ecf20Sopenharmony_ci#define MDIO_CHECK_CLR_ST	0
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev,
1468c2ecf20Sopenharmony_ci				 u32 cfg_reg, u32 set_val,
1478c2ecf20Sopenharmony_ci				 u32 st_reg, u32 st_msk, u8 check_st)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	u32 time_cnt;
1508c2ecf20Sopenharmony_ci	u32 reg_value;
1518c2ecf20Sopenharmony_ci	int ret;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	regmap_write(mdio_dev->subctrl_vbase, cfg_reg, set_val);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) {
1568c2ecf20Sopenharmony_ci		ret = regmap_read(mdio_dev->subctrl_vbase, st_reg, &reg_value);
1578c2ecf20Sopenharmony_ci		if (ret)
1588c2ecf20Sopenharmony_ci			return ret;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		reg_value &= st_msk;
1618c2ecf20Sopenharmony_ci		if ((!!check_st) == (!!reg_value))
1628c2ecf20Sopenharmony_ci			break;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if ((!!check_st) != (!!reg_value))
1668c2ecf20Sopenharmony_ci		return -EBUSY;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int hns_mdio_wait_ready(struct mii_bus *bus)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct hns_mdio_device *mdio_dev = bus->priv;
1748c2ecf20Sopenharmony_ci	u32 cmd_reg_value;
1758c2ecf20Sopenharmony_ci	int i;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* waitting for MDIO_COMMAND_REG 's mdio_start==0 */
1788c2ecf20Sopenharmony_ci	/* after that can do read or write*/
1798c2ecf20Sopenharmony_ci	for (i = 0; i < MDIO_TIMEOUT; i++) {
1808c2ecf20Sopenharmony_ci		cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev,
1818c2ecf20Sopenharmony_ci						 MDIO_COMMAND_REG,
1828c2ecf20Sopenharmony_ci						 MDIO_CMD_START_B);
1838c2ecf20Sopenharmony_ci		if (!cmd_reg_value)
1848c2ecf20Sopenharmony_ci			break;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	if ((i == MDIO_TIMEOUT) && cmd_reg_value)
1878c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev,
1938c2ecf20Sopenharmony_ci			       u8 is_c45, u8 op, u8 phy_id, u16 cmd)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	u32 cmd_reg_value;
1968c2ecf20Sopenharmony_ci	u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	cmd_reg_value = st << MDIO_CMD_ST_S;
1998c2ecf20Sopenharmony_ci	cmd_reg_value |= op << MDIO_CMD_OP_S;
2008c2ecf20Sopenharmony_ci	cmd_reg_value |=
2018c2ecf20Sopenharmony_ci		(phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S;
2028c2ecf20Sopenharmony_ci	cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S;
2038c2ecf20Sopenharmony_ci	cmd_reg_value |= 1 << MDIO_CMD_START_B;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/**
2098c2ecf20Sopenharmony_ci * hns_mdio_write - access phy register
2108c2ecf20Sopenharmony_ci * @bus: mdio bus
2118c2ecf20Sopenharmony_ci * @phy_id: phy id
2128c2ecf20Sopenharmony_ci * @regnum: register num
2138c2ecf20Sopenharmony_ci * @data: register value
2148c2ecf20Sopenharmony_ci *
2158c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistatic int hns_mdio_write(struct mii_bus *bus,
2188c2ecf20Sopenharmony_ci			  int phy_id, int regnum, u16 data)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int ret;
2218c2ecf20Sopenharmony_ci	struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
2228c2ecf20Sopenharmony_ci	u8 devad = ((regnum >> 16) & 0x1f);
2238c2ecf20Sopenharmony_ci	u8 is_c45 = !!(regnum & MII_ADDR_C45);
2248c2ecf20Sopenharmony_ci	u16 reg = (u16)(regnum & 0xffff);
2258c2ecf20Sopenharmony_ci	u8 op;
2268c2ecf20Sopenharmony_ci	u16 cmd_reg_cfg;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	dev_dbg(&bus->dev, "mdio write %s,base is %p\n",
2298c2ecf20Sopenharmony_ci		bus->id, mdio_dev->vbase);
2308c2ecf20Sopenharmony_ci	dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n",
2318c2ecf20Sopenharmony_ci		phy_id, is_c45, devad, reg, data);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* wait for ready */
2348c2ecf20Sopenharmony_ci	ret = hns_mdio_wait_ready(bus);
2358c2ecf20Sopenharmony_ci	if (ret) {
2368c2ecf20Sopenharmony_ci		dev_err(&bus->dev, "MDIO bus is busy\n");
2378c2ecf20Sopenharmony_ci		return ret;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (!is_c45) {
2418c2ecf20Sopenharmony_ci		cmd_reg_cfg = reg;
2428c2ecf20Sopenharmony_ci		op = MDIO_C22_WRITE;
2438c2ecf20Sopenharmony_ci	} else {
2448c2ecf20Sopenharmony_ci		/* config the cmd-reg to write addr*/
2458c2ecf20Sopenharmony_ci		MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M,
2468c2ecf20Sopenharmony_ci				   MDIO_ADDR_DATA_S, reg);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		hns_mdio_cmd_write(mdio_dev, is_c45,
2498c2ecf20Sopenharmony_ci				   MDIO_C45_WRITE_ADDR, phy_id, devad);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		/* check for read or write opt is finished */
2528c2ecf20Sopenharmony_ci		ret = hns_mdio_wait_ready(bus);
2538c2ecf20Sopenharmony_ci		if (ret) {
2548c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO bus is busy\n");
2558c2ecf20Sopenharmony_ci			return ret;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		/* config the data needed writing */
2598c2ecf20Sopenharmony_ci		cmd_reg_cfg = devad;
2608c2ecf20Sopenharmony_ci		op = MDIO_C45_WRITE_DATA;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M,
2648c2ecf20Sopenharmony_ci			   MDIO_WDATA_DATA_S, data);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/**
2728c2ecf20Sopenharmony_ci * hns_mdio_read - access phy register
2738c2ecf20Sopenharmony_ci * @bus: mdio bus
2748c2ecf20Sopenharmony_ci * @phy_id: phy id
2758c2ecf20Sopenharmony_ci * @regnum: register num
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * Return phy register value
2788c2ecf20Sopenharmony_ci */
2798c2ecf20Sopenharmony_cistatic int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int ret;
2828c2ecf20Sopenharmony_ci	u16 reg_val = 0;
2838c2ecf20Sopenharmony_ci	u8 devad = ((regnum >> 16) & 0x1f);
2848c2ecf20Sopenharmony_ci	u8 is_c45 = !!(regnum & MII_ADDR_C45);
2858c2ecf20Sopenharmony_ci	u16 reg = (u16)(regnum & 0xffff);
2868c2ecf20Sopenharmony_ci	struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	dev_dbg(&bus->dev, "mdio read %s,base is %p\n",
2898c2ecf20Sopenharmony_ci		bus->id, mdio_dev->vbase);
2908c2ecf20Sopenharmony_ci	dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n",
2918c2ecf20Sopenharmony_ci		phy_id, is_c45, devad, reg);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Step 1: wait for ready */
2948c2ecf20Sopenharmony_ci	ret = hns_mdio_wait_ready(bus);
2958c2ecf20Sopenharmony_ci	if (ret) {
2968c2ecf20Sopenharmony_ci		dev_err(&bus->dev, "MDIO bus is busy\n");
2978c2ecf20Sopenharmony_ci		return ret;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (!is_c45) {
3018c2ecf20Sopenharmony_ci		hns_mdio_cmd_write(mdio_dev, is_c45,
3028c2ecf20Sopenharmony_ci				   MDIO_C22_READ, phy_id, reg);
3038c2ecf20Sopenharmony_ci	} else {
3048c2ecf20Sopenharmony_ci		MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M,
3058c2ecf20Sopenharmony_ci				   MDIO_ADDR_DATA_S, reg);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		/* Step 2; config the cmd-reg to write addr*/
3088c2ecf20Sopenharmony_ci		hns_mdio_cmd_write(mdio_dev, is_c45,
3098c2ecf20Sopenharmony_ci				   MDIO_C45_WRITE_ADDR, phy_id, devad);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		/* Step 3: check for read or write opt is finished */
3128c2ecf20Sopenharmony_ci		ret = hns_mdio_wait_ready(bus);
3138c2ecf20Sopenharmony_ci		if (ret) {
3148c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO bus is busy\n");
3158c2ecf20Sopenharmony_ci			return ret;
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		hns_mdio_cmd_write(mdio_dev, is_c45,
3198c2ecf20Sopenharmony_ci				   MDIO_C45_READ, phy_id, devad);
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/
3238c2ecf20Sopenharmony_ci	/* check for read or write opt is finished */
3248c2ecf20Sopenharmony_ci	ret = hns_mdio_wait_ready(bus);
3258c2ecf20Sopenharmony_ci	if (ret) {
3268c2ecf20Sopenharmony_ci		dev_err(&bus->dev, "MDIO bus is busy\n");
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B);
3318c2ecf20Sopenharmony_ci	if (reg_val) {
3328c2ecf20Sopenharmony_ci		dev_err(&bus->dev, " ERROR! MDIO Read failed!\n");
3338c2ecf20Sopenharmony_ci		return -EBUSY;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Step 6; get out data*/
3378c2ecf20Sopenharmony_ci	reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG,
3388c2ecf20Sopenharmony_ci					  MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return reg_val;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/**
3448c2ecf20Sopenharmony_ci * hns_mdio_reset - reset mdio bus
3458c2ecf20Sopenharmony_ci * @bus: mdio bus
3468c2ecf20Sopenharmony_ci *
3478c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure
3488c2ecf20Sopenharmony_ci */
3498c2ecf20Sopenharmony_cistatic int hns_mdio_reset(struct mii_bus *bus)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
3528c2ecf20Sopenharmony_ci	const struct hns_mdio_sc_reg *sc_reg;
3538c2ecf20Sopenharmony_ci	int ret;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (dev_of_node(bus->parent)) {
3568c2ecf20Sopenharmony_ci		if (!mdio_dev->subctrl_vbase) {
3578c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "mdio sys ctl reg has not maped\n");
3588c2ecf20Sopenharmony_ci			return -ENODEV;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		sc_reg = &mdio_dev->sc_reg;
3628c2ecf20Sopenharmony_ci		/* 1. reset req, and read reset st check */
3638c2ecf20Sopenharmony_ci		ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_reset_req,
3648c2ecf20Sopenharmony_ci					    0x1, sc_reg->mdio_reset_st, 0x1,
3658c2ecf20Sopenharmony_ci					    MDIO_CHECK_SET_ST);
3668c2ecf20Sopenharmony_ci		if (ret) {
3678c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO reset fail\n");
3688c2ecf20Sopenharmony_ci			return ret;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		/* 2. dis clk, and read clk st check */
3728c2ecf20Sopenharmony_ci		ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_clk_dis,
3738c2ecf20Sopenharmony_ci					    0x1, sc_reg->mdio_clk_st, 0x1,
3748c2ecf20Sopenharmony_ci					    MDIO_CHECK_CLR_ST);
3758c2ecf20Sopenharmony_ci		if (ret) {
3768c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO dis clk fail\n");
3778c2ecf20Sopenharmony_ci			return ret;
3788c2ecf20Sopenharmony_ci		}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		/* 3. reset dreq, and read reset st check */
3818c2ecf20Sopenharmony_ci		ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_reset_dreq,
3828c2ecf20Sopenharmony_ci					    0x1, sc_reg->mdio_reset_st, 0x1,
3838c2ecf20Sopenharmony_ci					    MDIO_CHECK_CLR_ST);
3848c2ecf20Sopenharmony_ci		if (ret) {
3858c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO dis clk fail\n");
3868c2ecf20Sopenharmony_ci			return ret;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		/* 4. en clk, and read clk st check */
3908c2ecf20Sopenharmony_ci		ret = mdio_sc_cfg_reg_write(mdio_dev, sc_reg->mdio_clk_en,
3918c2ecf20Sopenharmony_ci					    0x1, sc_reg->mdio_clk_st, 0x1,
3928c2ecf20Sopenharmony_ci					    MDIO_CHECK_SET_ST);
3938c2ecf20Sopenharmony_ci		if (ret)
3948c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "MDIO en clk fail\n");
3958c2ecf20Sopenharmony_ci	} else if (is_acpi_node(bus->parent->fwnode)) {
3968c2ecf20Sopenharmony_ci		acpi_status s;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		s = acpi_evaluate_object(ACPI_HANDLE(bus->parent),
3998c2ecf20Sopenharmony_ci					 "_RST", NULL, NULL);
4008c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(s)) {
4018c2ecf20Sopenharmony_ci			dev_err(&bus->dev, "Reset failed, return:%#x\n", s);
4028c2ecf20Sopenharmony_ci			ret = -EBUSY;
4038c2ecf20Sopenharmony_ci		} else {
4048c2ecf20Sopenharmony_ci			ret = 0;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	} else {
4078c2ecf20Sopenharmony_ci		dev_err(&bus->dev, "Can not get cfg data from DT or ACPI\n");
4088c2ecf20Sopenharmony_ci		ret = -ENXIO;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	return ret;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/**
4148c2ecf20Sopenharmony_ci * hns_mdio_probe - probe mdio device
4158c2ecf20Sopenharmony_ci * @pdev: mdio platform device
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure
4188c2ecf20Sopenharmony_ci */
4198c2ecf20Sopenharmony_cistatic int hns_mdio_probe(struct platform_device *pdev)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct hns_mdio_device *mdio_dev;
4228c2ecf20Sopenharmony_ci	struct mii_bus *new_bus;
4238c2ecf20Sopenharmony_ci	int ret = -ENODEV;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!pdev) {
4268c2ecf20Sopenharmony_ci		dev_err(NULL, "pdev is NULL!\r\n");
4278c2ecf20Sopenharmony_ci		return -ENODEV;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL);
4318c2ecf20Sopenharmony_ci	if (!mdio_dev)
4328c2ecf20Sopenharmony_ci		return -ENOMEM;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	new_bus = devm_mdiobus_alloc(&pdev->dev);
4358c2ecf20Sopenharmony_ci	if (!new_bus) {
4368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "mdiobus_alloc fail!\n");
4378c2ecf20Sopenharmony_ci		return -ENOMEM;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	new_bus->name = MDIO_BUS_NAME;
4418c2ecf20Sopenharmony_ci	new_bus->read = hns_mdio_read;
4428c2ecf20Sopenharmony_ci	new_bus->write = hns_mdio_write;
4438c2ecf20Sopenharmony_ci	new_bus->reset = hns_mdio_reset;
4448c2ecf20Sopenharmony_ci	new_bus->priv = mdio_dev;
4458c2ecf20Sopenharmony_ci	new_bus->parent = &pdev->dev;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	mdio_dev->vbase = devm_platform_ioremap_resource(pdev, 0);
4488c2ecf20Sopenharmony_ci	if (IS_ERR(mdio_dev->vbase)) {
4498c2ecf20Sopenharmony_ci		ret = PTR_ERR(mdio_dev->vbase);
4508c2ecf20Sopenharmony_ci		return ret;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, new_bus);
4548c2ecf20Sopenharmony_ci	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%s", "Mii",
4558c2ecf20Sopenharmony_ci		 dev_name(&pdev->dev));
4568c2ecf20Sopenharmony_ci	if (dev_of_node(&pdev->dev)) {
4578c2ecf20Sopenharmony_ci		struct of_phandle_args reg_args;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
4608c2ecf20Sopenharmony_ci						       "subctrl-vbase",
4618c2ecf20Sopenharmony_ci						       4,
4628c2ecf20Sopenharmony_ci						       0,
4638c2ecf20Sopenharmony_ci						       &reg_args);
4648c2ecf20Sopenharmony_ci		if (!ret) {
4658c2ecf20Sopenharmony_ci			mdio_dev->subctrl_vbase =
4668c2ecf20Sopenharmony_ci				syscon_node_to_regmap(reg_args.np);
4678c2ecf20Sopenharmony_ci			if (IS_ERR(mdio_dev->subctrl_vbase)) {
4688c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev, "syscon_node_to_regmap error\n");
4698c2ecf20Sopenharmony_ci				mdio_dev->subctrl_vbase = NULL;
4708c2ecf20Sopenharmony_ci			} else {
4718c2ecf20Sopenharmony_ci				if (reg_args.args_count == 4) {
4728c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_en =
4738c2ecf20Sopenharmony_ci						(u16)reg_args.args[0];
4748c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_dis =
4758c2ecf20Sopenharmony_ci						(u16)reg_args.args[0] + 4;
4768c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_req =
4778c2ecf20Sopenharmony_ci						(u16)reg_args.args[1];
4788c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_dreq =
4798c2ecf20Sopenharmony_ci						(u16)reg_args.args[1] + 4;
4808c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_st =
4818c2ecf20Sopenharmony_ci						(u16)reg_args.args[2];
4828c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_st =
4838c2ecf20Sopenharmony_ci						(u16)reg_args.args[3];
4848c2ecf20Sopenharmony_ci				} else {
4858c2ecf20Sopenharmony_ci					/* for compatible */
4868c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_en =
4878c2ecf20Sopenharmony_ci						MDIO_SC_CLK_EN;
4888c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_dis =
4898c2ecf20Sopenharmony_ci						MDIO_SC_CLK_DIS;
4908c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_req =
4918c2ecf20Sopenharmony_ci						MDIO_SC_RESET_REQ;
4928c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_dreq =
4938c2ecf20Sopenharmony_ci						MDIO_SC_RESET_DREQ;
4948c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_clk_st =
4958c2ecf20Sopenharmony_ci						MDIO_SC_CLK_ST;
4968c2ecf20Sopenharmony_ci					mdio_dev->sc_reg.mdio_reset_st =
4978c2ecf20Sopenharmony_ci						MDIO_SC_RESET_ST;
4988c2ecf20Sopenharmony_ci				}
4998c2ecf20Sopenharmony_ci			}
5008c2ecf20Sopenharmony_ci		} else {
5018c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev, "find syscon ret = %#x\n", ret);
5028c2ecf20Sopenharmony_ci			mdio_dev->subctrl_vbase = NULL;
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
5068c2ecf20Sopenharmony_ci	} else if (is_acpi_node(pdev->dev.fwnode)) {
5078c2ecf20Sopenharmony_ci		/* Clear all the IRQ properties */
5088c2ecf20Sopenharmony_ci		memset(new_bus->irq, PHY_POLL, 4 * PHY_MAX_ADDR);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		/* Mask out all PHYs from auto probing. */
5118c2ecf20Sopenharmony_ci		new_bus->phy_mask = ~0;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		/* Register the MDIO bus */
5148c2ecf20Sopenharmony_ci		ret = mdiobus_register(new_bus);
5158c2ecf20Sopenharmony_ci	} else {
5168c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Can not get cfg data from DT or ACPI\n");
5178c2ecf20Sopenharmony_ci		ret = -ENXIO;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	if (ret) {
5218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cannot register as MDIO bus!\n");
5228c2ecf20Sopenharmony_ci		platform_set_drvdata(pdev, NULL);
5238c2ecf20Sopenharmony_ci		return ret;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return 0;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/**
5308c2ecf20Sopenharmony_ci * hns_mdio_remove - remove mdio device
5318c2ecf20Sopenharmony_ci * @pdev: mdio platform device
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure
5348c2ecf20Sopenharmony_ci */
5358c2ecf20Sopenharmony_cistatic int hns_mdio_remove(struct platform_device *pdev)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct mii_bus *bus;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	bus = platform_get_drvdata(pdev);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	mdiobus_unregister(bus);
5428c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic const struct of_device_id hns_mdio_match[] = {
5478c2ecf20Sopenharmony_ci	{.compatible = "hisilicon,mdio"},
5488c2ecf20Sopenharmony_ci	{.compatible = "hisilicon,hns-mdio"},
5498c2ecf20Sopenharmony_ci	{}
5508c2ecf20Sopenharmony_ci};
5518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hns_mdio_match);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic const struct acpi_device_id hns_mdio_acpi_match[] = {
5548c2ecf20Sopenharmony_ci	{ "HISI0141", 0 },
5558c2ecf20Sopenharmony_ci	{ },
5568c2ecf20Sopenharmony_ci};
5578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hns_mdio_acpi_match);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic struct platform_driver hns_mdio_driver = {
5608c2ecf20Sopenharmony_ci	.probe = hns_mdio_probe,
5618c2ecf20Sopenharmony_ci	.remove = hns_mdio_remove,
5628c2ecf20Sopenharmony_ci	.driver = {
5638c2ecf20Sopenharmony_ci		   .name = MDIO_DRV_NAME,
5648c2ecf20Sopenharmony_ci		   .of_match_table = hns_mdio_match,
5658c2ecf20Sopenharmony_ci		   .acpi_match_table = ACPI_PTR(hns_mdio_acpi_match),
5668c2ecf20Sopenharmony_ci		   },
5678c2ecf20Sopenharmony_ci};
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cimodule_platform_driver(hns_mdio_driver);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Huawei Tech. Co., Ltd.");
5738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon HNS MDIO driver");
5748c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" MDIO_DRV_NAME);
575