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