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