xref: /linux/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
1*a239b2b1SJijie Shao // SPDX-License-Identifier: GPL-2.0+
2*a239b2b1SJijie Shao // Copyright (c) 2024 Hisilicon Limited.
3*a239b2b1SJijie Shao 
4*a239b2b1SJijie Shao #include <linux/phy.h>
5*a239b2b1SJijie Shao #include "hbg_common.h"
6*a239b2b1SJijie Shao #include "hbg_hw.h"
7*a239b2b1SJijie Shao #include "hbg_mdio.h"
8*a239b2b1SJijie Shao #include "hbg_reg.h"
9*a239b2b1SJijie Shao 
10*a239b2b1SJijie Shao #define HBG_MAC_GET_PRIV(mac) ((struct hbg_priv *)(mac)->mdio_bus->priv)
11*a239b2b1SJijie Shao #define HBG_MII_BUS_GET_MAC(bus) (&((struct hbg_priv *)(bus)->priv)->mac)
12*a239b2b1SJijie Shao 
13*a239b2b1SJijie Shao #define HBG_MDIO_C22_MODE		0x1
14*a239b2b1SJijie Shao #define HBG_MDIO_C22_REG_WRITE		0x1
15*a239b2b1SJijie Shao #define HBG_MDIO_C22_REG_READ		0x2
16*a239b2b1SJijie Shao 
17*a239b2b1SJijie Shao #define HBG_MDIO_OP_TIMEOUT_US		(1 * 1000 * 1000)
18*a239b2b1SJijie Shao #define HBG_MDIO_OP_INTERVAL_US		(5 * 1000)
19*a239b2b1SJijie Shao 
20*a239b2b1SJijie Shao static void hbg_mdio_set_command(struct hbg_mac *mac, u32 cmd)
21*a239b2b1SJijie Shao {
22*a239b2b1SJijie Shao 	hbg_reg_write(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR, cmd);
23*a239b2b1SJijie Shao }
24*a239b2b1SJijie Shao 
25*a239b2b1SJijie Shao static void hbg_mdio_get_command(struct hbg_mac *mac, u32 *cmd)
26*a239b2b1SJijie Shao {
27*a239b2b1SJijie Shao 	*cmd = hbg_reg_read(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR);
28*a239b2b1SJijie Shao }
29*a239b2b1SJijie Shao 
30*a239b2b1SJijie Shao static void hbg_mdio_set_wdata_reg(struct hbg_mac *mac, u16 wdata_value)
31*a239b2b1SJijie Shao {
32*a239b2b1SJijie Shao 	hbg_reg_write_field(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_WDATA_ADDR,
33*a239b2b1SJijie Shao 			    HBG_REG_MDIO_WDATA_M, wdata_value);
34*a239b2b1SJijie Shao }
35*a239b2b1SJijie Shao 
36*a239b2b1SJijie Shao static u32 hbg_mdio_get_rdata_reg(struct hbg_mac *mac)
37*a239b2b1SJijie Shao {
38*a239b2b1SJijie Shao 	return hbg_reg_read_field(HBG_MAC_GET_PRIV(mac),
39*a239b2b1SJijie Shao 				  HBG_REG_MDIO_RDATA_ADDR,
40*a239b2b1SJijie Shao 				  HBG_REG_MDIO_WDATA_M);
41*a239b2b1SJijie Shao }
42*a239b2b1SJijie Shao 
43*a239b2b1SJijie Shao static int hbg_mdio_wait_ready(struct hbg_mac *mac)
44*a239b2b1SJijie Shao {
45*a239b2b1SJijie Shao 	struct hbg_priv *priv = HBG_MAC_GET_PRIV(mac);
46*a239b2b1SJijie Shao 	u32 cmd = 0;
47*a239b2b1SJijie Shao 	int ret;
48*a239b2b1SJijie Shao 
49*a239b2b1SJijie Shao 	ret = readl_poll_timeout(priv->io_base + HBG_REG_MDIO_COMMAND_ADDR, cmd,
50*a239b2b1SJijie Shao 				 !FIELD_GET(HBG_REG_MDIO_COMMAND_START_B, cmd),
51*a239b2b1SJijie Shao 				 HBG_MDIO_OP_INTERVAL_US,
52*a239b2b1SJijie Shao 				 HBG_MDIO_OP_TIMEOUT_US);
53*a239b2b1SJijie Shao 
54*a239b2b1SJijie Shao 	return ret ? -ETIMEDOUT : 0;
55*a239b2b1SJijie Shao }
56*a239b2b1SJijie Shao 
57*a239b2b1SJijie Shao static int hbg_mdio_cmd_send(struct hbg_mac *mac, u32 prt_addr, u32 dev_addr,
58*a239b2b1SJijie Shao 			     u32 type, u32 op_code)
59*a239b2b1SJijie Shao {
60*a239b2b1SJijie Shao 	u32 cmd = 0;
61*a239b2b1SJijie Shao 
62*a239b2b1SJijie Shao 	hbg_mdio_get_command(mac, &cmd);
63*a239b2b1SJijie Shao 	hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_ST_M, type);
64*a239b2b1SJijie Shao 	hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_OP_M, op_code);
65*a239b2b1SJijie Shao 	hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_PRTAD_M, prt_addr);
66*a239b2b1SJijie Shao 	hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_DEVAD_M, dev_addr);
67*a239b2b1SJijie Shao 
68*a239b2b1SJijie Shao 	/* if auto scan enabled, this value need fix to 0 */
69*a239b2b1SJijie Shao 	hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_START_B, 0x1);
70*a239b2b1SJijie Shao 
71*a239b2b1SJijie Shao 	hbg_mdio_set_command(mac, cmd);
72*a239b2b1SJijie Shao 
73*a239b2b1SJijie Shao 	/* wait operation complete and check the result */
74*a239b2b1SJijie Shao 	return hbg_mdio_wait_ready(mac);
75*a239b2b1SJijie Shao }
76*a239b2b1SJijie Shao 
77*a239b2b1SJijie Shao static int hbg_mdio_read22(struct mii_bus *bus, int phy_addr, int regnum)
78*a239b2b1SJijie Shao {
79*a239b2b1SJijie Shao 	struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus);
80*a239b2b1SJijie Shao 	int ret;
81*a239b2b1SJijie Shao 
82*a239b2b1SJijie Shao 	ret = hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE,
83*a239b2b1SJijie Shao 				HBG_MDIO_C22_REG_READ);
84*a239b2b1SJijie Shao 	if (ret)
85*a239b2b1SJijie Shao 		return ret;
86*a239b2b1SJijie Shao 
87*a239b2b1SJijie Shao 	return hbg_mdio_get_rdata_reg(mac);
88*a239b2b1SJijie Shao }
89*a239b2b1SJijie Shao 
90*a239b2b1SJijie Shao static int hbg_mdio_write22(struct mii_bus *bus, int phy_addr, int regnum,
91*a239b2b1SJijie Shao 			    u16 val)
92*a239b2b1SJijie Shao {
93*a239b2b1SJijie Shao 	struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus);
94*a239b2b1SJijie Shao 
95*a239b2b1SJijie Shao 	hbg_mdio_set_wdata_reg(mac, val);
96*a239b2b1SJijie Shao 	return hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE,
97*a239b2b1SJijie Shao 				 HBG_MDIO_C22_REG_WRITE);
98*a239b2b1SJijie Shao }
99*a239b2b1SJijie Shao 
100*a239b2b1SJijie Shao static void hbg_mdio_init_hw(struct hbg_priv *priv)
101*a239b2b1SJijie Shao {
102*a239b2b1SJijie Shao 	u32 freq = priv->dev_specs.mdio_frequency;
103*a239b2b1SJijie Shao 	struct hbg_mac *mac = &priv->mac;
104*a239b2b1SJijie Shao 	u32 cmd = 0;
105*a239b2b1SJijie Shao 
106*a239b2b1SJijie Shao 	cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_ST_M, HBG_MDIO_C22_MODE);
107*a239b2b1SJijie Shao 	cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_AUTO_SCAN_B, HBG_STATUS_DISABLE);
108*a239b2b1SJijie Shao 
109*a239b2b1SJijie Shao 	/* freq use two bits, which are stored in clk_sel and clk_sel_exp */
110*a239b2b1SJijie Shao 	cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_B, freq & 0x1);
111*a239b2b1SJijie Shao 	cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_EXP_B,
112*a239b2b1SJijie Shao 			  (freq >> 1) & 0x1);
113*a239b2b1SJijie Shao 
114*a239b2b1SJijie Shao 	hbg_mdio_set_command(mac, cmd);
115*a239b2b1SJijie Shao }
116*a239b2b1SJijie Shao 
117*a239b2b1SJijie Shao static void hbg_phy_adjust_link(struct net_device *netdev)
118*a239b2b1SJijie Shao {
119*a239b2b1SJijie Shao 	struct hbg_priv *priv = netdev_priv(netdev);
120*a239b2b1SJijie Shao 	struct phy_device *phydev = netdev->phydev;
121*a239b2b1SJijie Shao 	u32 speed;
122*a239b2b1SJijie Shao 
123*a239b2b1SJijie Shao 	if (phydev->link != priv->mac.link_status) {
124*a239b2b1SJijie Shao 		if (phydev->link) {
125*a239b2b1SJijie Shao 			switch (phydev->speed) {
126*a239b2b1SJijie Shao 			case SPEED_10:
127*a239b2b1SJijie Shao 				speed = HBG_PORT_MODE_SGMII_10M;
128*a239b2b1SJijie Shao 				break;
129*a239b2b1SJijie Shao 			case SPEED_100:
130*a239b2b1SJijie Shao 				speed = HBG_PORT_MODE_SGMII_100M;
131*a239b2b1SJijie Shao 				break;
132*a239b2b1SJijie Shao 			case SPEED_1000:
133*a239b2b1SJijie Shao 				speed = HBG_PORT_MODE_SGMII_1000M;
134*a239b2b1SJijie Shao 				break;
135*a239b2b1SJijie Shao 			default:
136*a239b2b1SJijie Shao 				return;
137*a239b2b1SJijie Shao 			}
138*a239b2b1SJijie Shao 
139*a239b2b1SJijie Shao 			priv->mac.speed = speed;
140*a239b2b1SJijie Shao 			priv->mac.duplex = phydev->duplex;
141*a239b2b1SJijie Shao 			priv->mac.autoneg = phydev->autoneg;
142*a239b2b1SJijie Shao 			hbg_hw_adjust_link(priv, speed, phydev->duplex);
143*a239b2b1SJijie Shao 		}
144*a239b2b1SJijie Shao 
145*a239b2b1SJijie Shao 		priv->mac.link_status = phydev->link;
146*a239b2b1SJijie Shao 		phy_print_status(phydev);
147*a239b2b1SJijie Shao 	}
148*a239b2b1SJijie Shao }
149*a239b2b1SJijie Shao 
150*a239b2b1SJijie Shao static void hbg_phy_disconnect(void *data)
151*a239b2b1SJijie Shao {
152*a239b2b1SJijie Shao 	phy_disconnect((struct phy_device *)data);
153*a239b2b1SJijie Shao }
154*a239b2b1SJijie Shao 
155*a239b2b1SJijie Shao static int hbg_phy_connect(struct hbg_priv *priv)
156*a239b2b1SJijie Shao {
157*a239b2b1SJijie Shao 	struct phy_device *phydev = priv->mac.phydev;
158*a239b2b1SJijie Shao 	struct device *dev = &priv->pdev->dev;
159*a239b2b1SJijie Shao 	int ret;
160*a239b2b1SJijie Shao 
161*a239b2b1SJijie Shao 	ret = phy_connect_direct(priv->netdev, phydev, hbg_phy_adjust_link,
162*a239b2b1SJijie Shao 				 PHY_INTERFACE_MODE_SGMII);
163*a239b2b1SJijie Shao 	if (ret)
164*a239b2b1SJijie Shao 		return dev_err_probe(dev, ret, "failed to connect phy\n");
165*a239b2b1SJijie Shao 
166*a239b2b1SJijie Shao 	ret = devm_add_action_or_reset(dev, hbg_phy_disconnect, phydev);
167*a239b2b1SJijie Shao 	if (ret)
168*a239b2b1SJijie Shao 		return ret;
169*a239b2b1SJijie Shao 
170*a239b2b1SJijie Shao 	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
171*a239b2b1SJijie Shao 	phy_attached_info(phydev);
172*a239b2b1SJijie Shao 
173*a239b2b1SJijie Shao 	return 0;
174*a239b2b1SJijie Shao }
175*a239b2b1SJijie Shao 
176*a239b2b1SJijie Shao void hbg_phy_start(struct hbg_priv *priv)
177*a239b2b1SJijie Shao {
178*a239b2b1SJijie Shao 	phy_start(priv->mac.phydev);
179*a239b2b1SJijie Shao }
180*a239b2b1SJijie Shao 
181*a239b2b1SJijie Shao void hbg_phy_stop(struct hbg_priv *priv)
182*a239b2b1SJijie Shao {
183*a239b2b1SJijie Shao 	phy_stop(priv->mac.phydev);
184*a239b2b1SJijie Shao }
185*a239b2b1SJijie Shao 
186*a239b2b1SJijie Shao int hbg_mdio_init(struct hbg_priv *priv)
187*a239b2b1SJijie Shao {
188*a239b2b1SJijie Shao 	struct device *dev = &priv->pdev->dev;
189*a239b2b1SJijie Shao 	struct hbg_mac *mac = &priv->mac;
190*a239b2b1SJijie Shao 	struct phy_device *phydev;
191*a239b2b1SJijie Shao 	struct mii_bus *mdio_bus;
192*a239b2b1SJijie Shao 	int ret;
193*a239b2b1SJijie Shao 
194*a239b2b1SJijie Shao 	mac->phy_addr = priv->dev_specs.phy_addr;
195*a239b2b1SJijie Shao 	mdio_bus = devm_mdiobus_alloc(dev);
196*a239b2b1SJijie Shao 	if (!mdio_bus)
197*a239b2b1SJijie Shao 		return dev_err_probe(dev, -ENOMEM,
198*a239b2b1SJijie Shao 				     "failed to alloc MDIO bus\n");
199*a239b2b1SJijie Shao 
200*a239b2b1SJijie Shao 	mdio_bus->parent = dev;
201*a239b2b1SJijie Shao 	mdio_bus->priv = priv;
202*a239b2b1SJijie Shao 	mdio_bus->phy_mask = ~(1 << mac->phy_addr);
203*a239b2b1SJijie Shao 	mdio_bus->name = "hibmcge mii bus";
204*a239b2b1SJijie Shao 	mac->mdio_bus = mdio_bus;
205*a239b2b1SJijie Shao 
206*a239b2b1SJijie Shao 	mdio_bus->read = hbg_mdio_read22;
207*a239b2b1SJijie Shao 	mdio_bus->write = hbg_mdio_write22;
208*a239b2b1SJijie Shao 	snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii", dev_name(dev));
209*a239b2b1SJijie Shao 
210*a239b2b1SJijie Shao 	ret = devm_mdiobus_register(dev, mdio_bus);
211*a239b2b1SJijie Shao 	if (ret)
212*a239b2b1SJijie Shao 		return dev_err_probe(dev, ret, "failed to register MDIO bus\n");
213*a239b2b1SJijie Shao 
214*a239b2b1SJijie Shao 	phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr);
215*a239b2b1SJijie Shao 	if (!phydev)
216*a239b2b1SJijie Shao 		return dev_err_probe(dev, -ENODEV,
217*a239b2b1SJijie Shao 				     "failed to get phy device\n");
218*a239b2b1SJijie Shao 
219*a239b2b1SJijie Shao 	mac->phydev = phydev;
220*a239b2b1SJijie Shao 	hbg_mdio_init_hw(priv);
221*a239b2b1SJijie Shao 	return hbg_phy_connect(priv);
222*a239b2b1SJijie Shao }
223