xref: /openbmc/linux/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
1d71d8381SJian Shen // SPDX-License-Identifier: GPL-2.0+
2d71d8381SJian Shen // Copyright (c) 2016-2017 Hisilicon Limited.
3256727daSSalil 
4256727daSSalil #include <linux/etherdevice.h>
5256727daSSalil #include <linux/kernel.h>
6a93f7fe1SJian Shen #include <linux/marvell_phy.h>
7256727daSSalil 
8256727daSSalil #include "hclge_cmd.h"
9256727daSSalil #include "hclge_main.h"
10256727daSSalil #include "hclge_mdio.h"
11256727daSSalil 
12256727daSSalil enum hclge_mdio_c22_op_seq {
13256727daSSalil 	HCLGE_MDIO_C22_WRITE = 1,
14256727daSSalil 	HCLGE_MDIO_C22_READ = 2
15256727daSSalil };
16256727daSSalil 
17256727daSSalil #define HCLGE_MDIO_CTRL_START_B		0
18256727daSSalil #define HCLGE_MDIO_CTRL_ST_S		1
19256727daSSalil #define HCLGE_MDIO_CTRL_ST_M		(0x3 << HCLGE_MDIO_CTRL_ST_S)
20256727daSSalil #define HCLGE_MDIO_CTRL_OP_S		3
21256727daSSalil #define HCLGE_MDIO_CTRL_OP_M		(0x3 << HCLGE_MDIO_CTRL_OP_S)
22256727daSSalil 
23256727daSSalil #define HCLGE_MDIO_PHYID_S		0
24256727daSSalil #define HCLGE_MDIO_PHYID_M		(0x1f << HCLGE_MDIO_PHYID_S)
25256727daSSalil 
26256727daSSalil #define HCLGE_MDIO_PHYREG_S		0
27256727daSSalil #define HCLGE_MDIO_PHYREG_M		(0x1f << HCLGE_MDIO_PHYREG_S)
28256727daSSalil 
29256727daSSalil #define HCLGE_MDIO_STA_B		0
30256727daSSalil 
31256727daSSalil struct hclge_mdio_cfg_cmd {
32256727daSSalil 	u8 ctrl_bit;
33256727daSSalil 	u8 phyid;
34256727daSSalil 	u8 phyad;
35256727daSSalil 	u8 rsvd;
36256727daSSalil 	__le16 reserve;
37256727daSSalil 	__le16 data_wr;
38256727daSSalil 	__le16 data_rd;
39256727daSSalil 	__le16 sta;
40256727daSSalil };
41256727daSSalil 
hclge_mdio_write(struct mii_bus * bus,int phyid,int regnum,u16 data)42256727daSSalil static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum,
43256727daSSalil 			    u16 data)
44256727daSSalil {
45256727daSSalil 	struct hclge_mdio_cfg_cmd *mdio_cmd;
46256727daSSalil 	struct hclge_dev *hdev = bus->priv;
47256727daSSalil 	struct hclge_desc desc;
48256727daSSalil 	int ret;
49256727daSSalil 
50eaa5607dSJie Wang 	if (test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state))
51ad0ecaefSGuangbin Huang 		return -EBUSY;
52b50ae26cSPeng Li 
53256727daSSalil 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false);
54256727daSSalil 
55256727daSSalil 	mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data;
56256727daSSalil 
57e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M,
58ebaf1908SWeihang Li 			HCLGE_MDIO_PHYID_S, (u32)phyid);
59e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M,
60ebaf1908SWeihang Li 			HCLGE_MDIO_PHYREG_S, (u32)regnum);
61256727daSSalil 
62e4e87715SPeng Li 	hnae3_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1);
63e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M,
64256727daSSalil 			HCLGE_MDIO_CTRL_ST_S, 1);
65e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M,
66256727daSSalil 			HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_WRITE);
67256727daSSalil 
68256727daSSalil 	mdio_cmd->data_wr = cpu_to_le16(data);
69256727daSSalil 
70256727daSSalil 	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
71256727daSSalil 	if (ret) {
72256727daSSalil 		dev_err(&hdev->pdev->dev,
73256727daSSalil 			"mdio write fail when sending cmd, status is %d.\n",
74256727daSSalil 			ret);
75256727daSSalil 		return ret;
76256727daSSalil 	}
77256727daSSalil 
78256727daSSalil 	return 0;
79256727daSSalil }
80256727daSSalil 
hclge_mdio_read(struct mii_bus * bus,int phyid,int regnum)81256727daSSalil static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum)
82256727daSSalil {
83256727daSSalil 	struct hclge_mdio_cfg_cmd *mdio_cmd;
84256727daSSalil 	struct hclge_dev *hdev = bus->priv;
85256727daSSalil 	struct hclge_desc desc;
86256727daSSalil 	int ret;
87256727daSSalil 
88eaa5607dSJie Wang 	if (test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state))
89ad0ecaefSGuangbin Huang 		return -EBUSY;
90b50ae26cSPeng Li 
91256727daSSalil 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true);
92256727daSSalil 
93256727daSSalil 	mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data;
94256727daSSalil 
95e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M,
96ebaf1908SWeihang Li 			HCLGE_MDIO_PHYID_S, (u32)phyid);
97e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M,
98ebaf1908SWeihang Li 			HCLGE_MDIO_PHYREG_S, (u32)regnum);
99256727daSSalil 
100e4e87715SPeng Li 	hnae3_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1);
101e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M,
102256727daSSalil 			HCLGE_MDIO_CTRL_ST_S, 1);
103e4e87715SPeng Li 	hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M,
104256727daSSalil 			HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_READ);
105256727daSSalil 
106256727daSSalil 	/* Read out phy data */
107256727daSSalil 	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
108256727daSSalil 	if (ret) {
109256727daSSalil 		dev_err(&hdev->pdev->dev,
110256727daSSalil 			"mdio read fail when get data, status is %d.\n",
111256727daSSalil 			ret);
112256727daSSalil 		return ret;
113256727daSSalil 	}
114256727daSSalil 
115e4e87715SPeng Li 	if (hnae3_get_bit(le16_to_cpu(mdio_cmd->sta), HCLGE_MDIO_STA_B)) {
116256727daSSalil 		dev_err(&hdev->pdev->dev, "mdio read data error\n");
117256727daSSalil 		return -EIO;
118256727daSSalil 	}
119256727daSSalil 
120256727daSSalil 	return le16_to_cpu(mdio_cmd->data_rd);
121256727daSSalil }
122256727daSSalil 
hclge_mac_mdio_config(struct hclge_dev * hdev)123256727daSSalil int hclge_mac_mdio_config(struct hclge_dev *hdev)
124256727daSSalil {
1252d0075b4SJian Shen #define PHY_INEXISTENT	255
1262d0075b4SJian Shen 
127256727daSSalil 	struct hclge_mac *mac = &hdev->hw.mac;
128256727daSSalil 	struct phy_device *phydev;
129256727daSSalil 	struct mii_bus *mdio_bus;
130256727daSSalil 	int ret;
131256727daSSalil 
1322d0075b4SJian Shen 	if (hdev->hw.mac.phy_addr == PHY_INEXISTENT) {
1332d0075b4SJian Shen 		dev_info(&hdev->pdev->dev,
1342d0075b4SJian Shen 			 "no phy device is connected to mdio bus\n");
1352d0075b4SJian Shen 		return 0;
1362d0075b4SJian Shen 	} else if (hdev->hw.mac.phy_addr >= PHY_MAX_ADDR) {
137adcf738bSGuojia Liao 		dev_err(&hdev->pdev->dev, "phy_addr(%u) is too large.\n",
138c5ef83cbSHuazhong Tan 			hdev->hw.mac.phy_addr);
139c5ef83cbSHuazhong Tan 		return -EINVAL;
140c5ef83cbSHuazhong Tan 	}
141256727daSSalil 
142256727daSSalil 	mdio_bus = devm_mdiobus_alloc(&hdev->pdev->dev);
143256727daSSalil 	if (!mdio_bus)
144256727daSSalil 		return -ENOMEM;
145256727daSSalil 
146256727daSSalil 	mdio_bus->name = "hisilicon MII bus";
147256727daSSalil 	mdio_bus->read = hclge_mdio_read;
148256727daSSalil 	mdio_bus->write = hclge_mdio_write;
149256727daSSalil 	snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii",
150256727daSSalil 		 dev_name(&hdev->pdev->dev));
151256727daSSalil 
152256727daSSalil 	mdio_bus->parent = &hdev->pdev->dev;
153256727daSSalil 	mdio_bus->priv = hdev;
154256727daSSalil 	mdio_bus->phy_mask = ~(1 << mac->phy_addr);
155256727daSSalil 	ret = mdiobus_register(mdio_bus);
156256727daSSalil 	if (ret) {
157256727daSSalil 		dev_err(mdio_bus->parent,
1581f4982efSHuazhong Tan 			"failed to register MDIO bus, ret = %d\n", ret);
159256727daSSalil 		return ret;
160256727daSSalil 	}
161256727daSSalil 
162256727daSSalil 	phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr);
163752b0694SSalil Mehta 	if (!phydev) {
164256727daSSalil 		dev_err(mdio_bus->parent, "Failed to get phy device\n");
165256727daSSalil 		mdiobus_unregister(mdio_bus);
166256727daSSalil 		return -EIO;
167256727daSSalil 	}
168256727daSSalil 
169256727daSSalil 	mac->phydev = phydev;
170256727daSSalil 	mac->mdio_bus = mdio_bus;
171256727daSSalil 
172256727daSSalil 	return 0;
173256727daSSalil }
174256727daSSalil 
hclge_mac_adjust_link(struct net_device * netdev)175256727daSSalil static void hclge_mac_adjust_link(struct net_device *netdev)
176256727daSSalil {
177256727daSSalil 	struct hnae3_handle *h = *((void **)netdev_priv(netdev));
178256727daSSalil 	struct hclge_vport *vport = hclge_get_vport(h);
179256727daSSalil 	struct hclge_dev *hdev = vport->back;
180256727daSSalil 	int duplex, speed;
181256727daSSalil 	int ret;
182256727daSSalil 
1830ad5ea5dSPeng Li 	/* When phy link down, do nothing */
1840ad5ea5dSPeng Li 	if (netdev->phydev->link == 0)
1850ad5ea5dSPeng Li 		return;
1860ad5ea5dSPeng Li 
187256727daSSalil 	speed = netdev->phydev->speed;
188256727daSSalil 	duplex = netdev->phydev->duplex;
189256727daSSalil 
1900f032f93SHao Chen 	ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex, 0);
191256727daSSalil 	if (ret)
192256727daSSalil 		netdev_err(netdev, "failed to adjust link.\n");
1931770a7a3SPeng Li 
194*1d2f4a73SPeiyang Wang 	hdev->hw.mac.req_speed = (u32)speed;
195*1d2f4a73SPeiyang Wang 	hdev->hw.mac.req_duplex = (u8)duplex;
196*1d2f4a73SPeiyang Wang 
1971770a7a3SPeng Li 	ret = hclge_cfg_flowctrl(hdev);
1981770a7a3SPeng Li 	if (ret)
1991770a7a3SPeng Li 		netdev_err(netdev, "failed to configure flow control.\n");
200256727daSSalil }
201256727daSSalil 
hclge_mac_connect_phy(struct hnae3_handle * handle)202c8a8045bSHuazhong Tan int hclge_mac_connect_phy(struct hnae3_handle *handle)
203256727daSSalil {
204c8a8045bSHuazhong Tan 	struct hclge_vport *vport = hclge_get_vport(handle);
205c8a8045bSHuazhong Tan 	struct hclge_dev *hdev = vport->back;
206256727daSSalil 	struct net_device *netdev = hdev->vport[0].nic.netdev;
207256727daSSalil 	struct phy_device *phydev = hdev->hw.mac.phydev;
2083c1bcc86SAndrew Lunn 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
209256727daSSalil 	int ret;
210256727daSSalil 
211256727daSSalil 	if (!phydev)
212256727daSSalil 		return 0;
213256727daSSalil 
2143c1bcc86SAndrew Lunn 	linkmode_clear_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
21560081dccSJian Shen 
216a93f7fe1SJian Shen 	phydev->dev_flags |= MARVELL_PHY_LED0_LINK_LED1_ACTIVE;
217a93f7fe1SJian Shen 
218256727daSSalil 	ret = phy_connect_direct(netdev, phydev,
219256727daSSalil 				 hclge_mac_adjust_link,
220256727daSSalil 				 PHY_INTERFACE_MODE_SGMII);
221256727daSSalil 	if (ret) {
222256727daSSalil 		netdev_err(netdev, "phy_connect_direct err.\n");
223256727daSSalil 		return ret;
224256727daSSalil 	}
225256727daSSalil 
226f18635d5SJian Shen 	linkmode_copy(mask, hdev->hw.mac.supported);
2273c1bcc86SAndrew Lunn 	linkmode_and(phydev->supported, phydev->supported, mask);
228f18635d5SJian Shen 	linkmode_copy(phydev->advertising, phydev->supported);
229439adf88SFuyun Liang 
230bc3781edSYonglong Liu 	/* supported flag is Pause and Asym Pause, but default advertising
231bc3781edSYonglong Liu 	 * should be rx on, tx on, so need clear Asym Pause in advertising
232bc3781edSYonglong Liu 	 * flag
233bc3781edSYonglong Liu 	 */
234bc3781edSYonglong Liu 	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
235bc3781edSYonglong Liu 			   phydev->advertising);
236bc3781edSYonglong Liu 
2371bef61fcSYonglong Liu 	phy_attached_info(phydev);
2381bef61fcSYonglong Liu 
239256727daSSalil 	return 0;
240256727daSSalil }
241256727daSSalil 
hclge_mac_disconnect_phy(struct hnae3_handle * handle)242c8a8045bSHuazhong Tan void hclge_mac_disconnect_phy(struct hnae3_handle *handle)
243b01b7cf1SFuyun Liang {
244c8a8045bSHuazhong Tan 	struct hclge_vport *vport = hclge_get_vport(handle);
245c8a8045bSHuazhong Tan 	struct hclge_dev *hdev = vport->back;
246b01b7cf1SFuyun Liang 	struct phy_device *phydev = hdev->hw.mac.phydev;
247b01b7cf1SFuyun Liang 
248b01b7cf1SFuyun Liang 	if (!phydev)
249b01b7cf1SFuyun Liang 		return;
250b01b7cf1SFuyun Liang 
251b01b7cf1SFuyun Liang 	phy_disconnect(phydev);
252b01b7cf1SFuyun Liang }
253b01b7cf1SFuyun Liang 
hclge_mac_start_phy(struct hclge_dev * hdev)254b01b7cf1SFuyun Liang void hclge_mac_start_phy(struct hclge_dev *hdev)
255b01b7cf1SFuyun Liang {
256b01b7cf1SFuyun Liang 	struct phy_device *phydev = hdev->hw.mac.phydev;
257b01b7cf1SFuyun Liang 
258b01b7cf1SFuyun Liang 	if (!phydev)
259b01b7cf1SFuyun Liang 		return;
260b01b7cf1SFuyun Liang 
261472497d0SYufeng Mo 	phy_loopback(phydev, false);
262472497d0SYufeng Mo 
263b01b7cf1SFuyun Liang 	phy_start(phydev);
264b01b7cf1SFuyun Liang }
265b01b7cf1SFuyun Liang 
hclge_mac_stop_phy(struct hclge_dev * hdev)266256727daSSalil void hclge_mac_stop_phy(struct hclge_dev *hdev)
267256727daSSalil {
268256727daSSalil 	struct net_device *netdev = hdev->vport[0].nic.netdev;
269256727daSSalil 	struct phy_device *phydev = netdev->phydev;
270256727daSSalil 
271256727daSSalil 	if (!phydev)
272256727daSSalil 		return;
273256727daSSalil 
274256727daSSalil 	phy_stop(phydev);
275256727daSSalil }
276024712f5SGuangbin Huang 
hclge_read_phy_reg(struct hclge_dev * hdev,u16 reg_addr)277024712f5SGuangbin Huang u16 hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr)
278024712f5SGuangbin Huang {
279024712f5SGuangbin Huang 	struct hclge_phy_reg_cmd *req;
280024712f5SGuangbin Huang 	struct hclge_desc desc;
281024712f5SGuangbin Huang 	int ret;
282024712f5SGuangbin Huang 
283024712f5SGuangbin Huang 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, true);
284024712f5SGuangbin Huang 
285024712f5SGuangbin Huang 	req = (struct hclge_phy_reg_cmd *)desc.data;
286024712f5SGuangbin Huang 	req->reg_addr = cpu_to_le16(reg_addr);
287024712f5SGuangbin Huang 
288024712f5SGuangbin Huang 	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
289024712f5SGuangbin Huang 	if (ret)
290024712f5SGuangbin Huang 		dev_err(&hdev->pdev->dev,
291024712f5SGuangbin Huang 			"failed to read phy reg, ret = %d.\n", ret);
292024712f5SGuangbin Huang 
293024712f5SGuangbin Huang 	return le16_to_cpu(req->reg_val);
294024712f5SGuangbin Huang }
295024712f5SGuangbin Huang 
hclge_write_phy_reg(struct hclge_dev * hdev,u16 reg_addr,u16 val)296024712f5SGuangbin Huang int hclge_write_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 val)
297024712f5SGuangbin Huang {
298024712f5SGuangbin Huang 	struct hclge_phy_reg_cmd *req;
299024712f5SGuangbin Huang 	struct hclge_desc desc;
300024712f5SGuangbin Huang 	int ret;
301024712f5SGuangbin Huang 
302024712f5SGuangbin Huang 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, false);
303024712f5SGuangbin Huang 
304024712f5SGuangbin Huang 	req = (struct hclge_phy_reg_cmd *)desc.data;
305024712f5SGuangbin Huang 	req->reg_addr = cpu_to_le16(reg_addr);
306024712f5SGuangbin Huang 	req->reg_val = cpu_to_le16(val);
307024712f5SGuangbin Huang 
308024712f5SGuangbin Huang 	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
309024712f5SGuangbin Huang 	if (ret)
310024712f5SGuangbin Huang 		dev_err(&hdev->pdev->dev,
311024712f5SGuangbin Huang 			"failed to write phy reg, ret = %d.\n", ret);
312024712f5SGuangbin Huang 
313024712f5SGuangbin Huang 	return ret;
314024712f5SGuangbin Huang }
315