1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright (c) 2016-2017 Hisilicon Limited. |
3 | |
4 | #include <linux/etherdevice.h> |
5 | #include <linux/kernel.h> |
6 | #include <linux/marvell_phy.h> |
7 | |
8 | #include "hclge_cmd.h" |
9 | #include "hclge_main.h" |
10 | #include "hclge_mdio.h" |
11 | |
12 | enum hclge_mdio_c22_op_seq { |
13 | HCLGE_MDIO_C22_WRITE = 1, |
14 | HCLGE_MDIO_C22_READ = 2 |
15 | }; |
16 | |
17 | #define HCLGE_MDIO_CTRL_START_B 0 |
18 | #define HCLGE_MDIO_CTRL_ST_S 1 |
19 | #define HCLGE_MDIO_CTRL_ST_M (0x3 << HCLGE_MDIO_CTRL_ST_S) |
20 | #define HCLGE_MDIO_CTRL_OP_S 3 |
21 | #define HCLGE_MDIO_CTRL_OP_M (0x3 << HCLGE_MDIO_CTRL_OP_S) |
22 | |
23 | #define HCLGE_MDIO_PHYID_S 0 |
24 | #define HCLGE_MDIO_PHYID_M (0x1f << HCLGE_MDIO_PHYID_S) |
25 | |
26 | #define HCLGE_MDIO_PHYREG_S 0 |
27 | #define HCLGE_MDIO_PHYREG_M (0x1f << HCLGE_MDIO_PHYREG_S) |
28 | |
29 | #define HCLGE_MDIO_STA_B 0 |
30 | |
31 | struct hclge_mdio_cfg_cmd { |
32 | u8 ctrl_bit; |
33 | u8 phyid; |
34 | u8 phyad; |
35 | u8 rsvd; |
36 | __le16 reserve; |
37 | __le16 data_wr; |
38 | __le16 data_rd; |
39 | __le16 sta; |
40 | }; |
41 | |
42 | static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum, |
43 | u16 data) |
44 | { |
45 | struct hclge_mdio_cfg_cmd *mdio_cmd; |
46 | struct hclge_dev *hdev = bus->priv; |
47 | struct hclge_desc desc; |
48 | int ret; |
49 | |
50 | if (test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state)) |
51 | return -EBUSY; |
52 | |
53 | hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false); |
54 | |
55 | mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; |
56 | |
57 | hnae3_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, |
58 | HCLGE_MDIO_PHYID_S, (u32)phyid); |
59 | hnae3_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, |
60 | HCLGE_MDIO_PHYREG_S, (u32)regnum); |
61 | |
62 | hnae3_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); |
63 | hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, |
64 | HCLGE_MDIO_CTRL_ST_S, 1); |
65 | hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, |
66 | HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_WRITE); |
67 | |
68 | mdio_cmd->data_wr = cpu_to_le16(data); |
69 | |
70 | ret = hclge_cmd_send(hw: &hdev->hw, desc: &desc, num: 1); |
71 | if (ret) { |
72 | dev_err(&hdev->pdev->dev, |
73 | "mdio write fail when sending cmd, status is %d.\n" , |
74 | ret); |
75 | return ret; |
76 | } |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum) |
82 | { |
83 | struct hclge_mdio_cfg_cmd *mdio_cmd; |
84 | struct hclge_dev *hdev = bus->priv; |
85 | struct hclge_desc desc; |
86 | int ret; |
87 | |
88 | if (test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state)) |
89 | return -EBUSY; |
90 | |
91 | hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true); |
92 | |
93 | mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; |
94 | |
95 | hnae3_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, |
96 | HCLGE_MDIO_PHYID_S, (u32)phyid); |
97 | hnae3_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, |
98 | HCLGE_MDIO_PHYREG_S, (u32)regnum); |
99 | |
100 | hnae3_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); |
101 | hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, |
102 | HCLGE_MDIO_CTRL_ST_S, 1); |
103 | hnae3_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, |
104 | HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_READ); |
105 | |
106 | /* Read out phy data */ |
107 | ret = hclge_cmd_send(hw: &hdev->hw, desc: &desc, num: 1); |
108 | if (ret) { |
109 | dev_err(&hdev->pdev->dev, |
110 | "mdio read fail when get data, status is %d.\n" , |
111 | ret); |
112 | return ret; |
113 | } |
114 | |
115 | if (hnae3_get_bit(le16_to_cpu(mdio_cmd->sta), HCLGE_MDIO_STA_B)) { |
116 | dev_err(&hdev->pdev->dev, "mdio read data error\n" ); |
117 | return -EIO; |
118 | } |
119 | |
120 | return le16_to_cpu(mdio_cmd->data_rd); |
121 | } |
122 | |
123 | int hclge_mac_mdio_config(struct hclge_dev *hdev) |
124 | { |
125 | #define PHY_INEXISTENT 255 |
126 | |
127 | struct hclge_mac *mac = &hdev->hw.mac; |
128 | struct phy_device *phydev; |
129 | struct mii_bus *mdio_bus; |
130 | int ret; |
131 | |
132 | if (hdev->hw.mac.phy_addr == PHY_INEXISTENT) { |
133 | dev_info(&hdev->pdev->dev, |
134 | "no phy device is connected to mdio bus\n" ); |
135 | return 0; |
136 | } else if (hdev->hw.mac.phy_addr >= PHY_MAX_ADDR) { |
137 | dev_err(&hdev->pdev->dev, "phy_addr(%u) is too large.\n" , |
138 | hdev->hw.mac.phy_addr); |
139 | return -EINVAL; |
140 | } |
141 | |
142 | mdio_bus = devm_mdiobus_alloc(dev: &hdev->pdev->dev); |
143 | if (!mdio_bus) |
144 | return -ENOMEM; |
145 | |
146 | mdio_bus->name = "hisilicon MII bus" ; |
147 | mdio_bus->read = hclge_mdio_read; |
148 | mdio_bus->write = hclge_mdio_write; |
149 | snprintf(buf: mdio_bus->id, MII_BUS_ID_SIZE, fmt: "%s-%s" , "mii" , |
150 | dev_name(dev: &hdev->pdev->dev)); |
151 | |
152 | mdio_bus->parent = &hdev->pdev->dev; |
153 | mdio_bus->priv = hdev; |
154 | mdio_bus->phy_mask = ~(1 << mac->phy_addr); |
155 | ret = mdiobus_register(mdio_bus); |
156 | if (ret) { |
157 | dev_err(mdio_bus->parent, |
158 | "failed to register MDIO bus, ret = %d\n" , ret); |
159 | return ret; |
160 | } |
161 | |
162 | phydev = mdiobus_get_phy(bus: mdio_bus, addr: mac->phy_addr); |
163 | if (!phydev) { |
164 | dev_err(mdio_bus->parent, "Failed to get phy device\n" ); |
165 | mdiobus_unregister(bus: mdio_bus); |
166 | return -EIO; |
167 | } |
168 | |
169 | mac->phydev = phydev; |
170 | mac->mdio_bus = mdio_bus; |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | static void hclge_mac_adjust_link(struct net_device *netdev) |
176 | { |
177 | struct hnae3_handle *h = *((void **)netdev_priv(dev: netdev)); |
178 | struct hclge_vport *vport = hclge_get_vport(handle: h); |
179 | struct hclge_dev *hdev = vport->back; |
180 | int duplex, speed; |
181 | int ret; |
182 | |
183 | /* When phy link down, do nothing */ |
184 | if (netdev->phydev->link == 0) |
185 | return; |
186 | |
187 | speed = netdev->phydev->speed; |
188 | duplex = netdev->phydev->duplex; |
189 | |
190 | ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex, lane_num: 0); |
191 | if (ret) |
192 | netdev_err(dev: netdev, format: "failed to adjust link.\n" ); |
193 | |
194 | ret = hclge_cfg_flowctrl(hdev); |
195 | if (ret) |
196 | netdev_err(dev: netdev, format: "failed to configure flow control.\n" ); |
197 | } |
198 | |
199 | int hclge_mac_connect_phy(struct hnae3_handle *handle) |
200 | { |
201 | struct hclge_vport *vport = hclge_get_vport(handle); |
202 | struct hclge_dev *hdev = vport->back; |
203 | struct net_device *netdev = hdev->vport[0].nic.netdev; |
204 | struct phy_device *phydev = hdev->hw.mac.phydev; |
205 | __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; |
206 | int ret; |
207 | |
208 | if (!phydev) |
209 | return 0; |
210 | |
211 | linkmode_clear_bit(nr: ETHTOOL_LINK_MODE_FIBRE_BIT, addr: phydev->supported); |
212 | |
213 | phydev->dev_flags |= MARVELL_PHY_LED0_LINK_LED1_ACTIVE; |
214 | |
215 | ret = phy_connect_direct(dev: netdev, phydev, |
216 | handler: hclge_mac_adjust_link, |
217 | interface: PHY_INTERFACE_MODE_SGMII); |
218 | if (ret) { |
219 | netdev_err(dev: netdev, format: "phy_connect_direct err.\n" ); |
220 | return ret; |
221 | } |
222 | |
223 | linkmode_copy(dst: mask, src: hdev->hw.mac.supported); |
224 | linkmode_and(dst: phydev->supported, a: phydev->supported, b: mask); |
225 | linkmode_copy(dst: phydev->advertising, src: phydev->supported); |
226 | |
227 | /* supported flag is Pause and Asym Pause, but default advertising |
228 | * should be rx on, tx on, so need clear Asym Pause in advertising |
229 | * flag |
230 | */ |
231 | linkmode_clear_bit(nr: ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
232 | addr: phydev->advertising); |
233 | |
234 | phy_attached_info(phydev); |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | void hclge_mac_disconnect_phy(struct hnae3_handle *handle) |
240 | { |
241 | struct hclge_vport *vport = hclge_get_vport(handle); |
242 | struct hclge_dev *hdev = vport->back; |
243 | struct phy_device *phydev = hdev->hw.mac.phydev; |
244 | |
245 | if (!phydev) |
246 | return; |
247 | |
248 | phy_disconnect(phydev); |
249 | } |
250 | |
251 | void hclge_mac_start_phy(struct hclge_dev *hdev) |
252 | { |
253 | struct phy_device *phydev = hdev->hw.mac.phydev; |
254 | |
255 | if (!phydev) |
256 | return; |
257 | |
258 | phy_loopback(phydev, enable: false); |
259 | |
260 | phy_start(phydev); |
261 | } |
262 | |
263 | void hclge_mac_stop_phy(struct hclge_dev *hdev) |
264 | { |
265 | struct net_device *netdev = hdev->vport[0].nic.netdev; |
266 | struct phy_device *phydev = netdev->phydev; |
267 | |
268 | if (!phydev) |
269 | return; |
270 | |
271 | phy_stop(phydev); |
272 | } |
273 | |
274 | u16 hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr) |
275 | { |
276 | struct hclge_phy_reg_cmd *req; |
277 | struct hclge_desc desc; |
278 | int ret; |
279 | |
280 | hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, true); |
281 | |
282 | req = (struct hclge_phy_reg_cmd *)desc.data; |
283 | req->reg_addr = cpu_to_le16(reg_addr); |
284 | |
285 | ret = hclge_cmd_send(hw: &hdev->hw, desc: &desc, num: 1); |
286 | if (ret) |
287 | dev_err(&hdev->pdev->dev, |
288 | "failed to read phy reg, ret = %d.\n" , ret); |
289 | |
290 | return le16_to_cpu(req->reg_val); |
291 | } |
292 | |
293 | int hclge_write_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 val) |
294 | { |
295 | struct hclge_phy_reg_cmd *req; |
296 | struct hclge_desc desc; |
297 | int ret; |
298 | |
299 | hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, false); |
300 | |
301 | req = (struct hclge_phy_reg_cmd *)desc.data; |
302 | req->reg_addr = cpu_to_le16(reg_addr); |
303 | req->reg_val = cpu_to_le16(val); |
304 | |
305 | ret = hclge_cmd_send(hw: &hdev->hw, desc: &desc, num: 1); |
306 | if (ret) |
307 | dev_err(&hdev->pdev->dev, |
308 | "failed to write phy reg, ret = %d.\n" , ret); |
309 | |
310 | return ret; |
311 | } |
312 | |