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, &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						       &reg_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