Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(C) 2023 Mucse IC Design Ltd.
3 : : */
4 : :
5 : : #include <rte_alarm.h>
6 : :
7 : : #include "base/rnp_mac_regs.h"
8 : : #include "base/rnp_dma_regs.h"
9 : : #include "base/rnp_mac.h"
10 : : #include "base/rnp_fw_cmd.h"
11 : : #include "base/rnp_mbx_fw.h"
12 : :
13 : : #include "rnp.h"
14 : : #include "rnp_rxtx.h"
15 : : #include "rnp_link.h"
16 : :
17 : : static void
18 : 0 : rnp_link_flow_setup(struct rnp_eth_port *port)
19 : : {
20 : 0 : struct rnp_hw *hw = port->hw;
21 : : u32 ctrl = 0;
22 : : u16 lane = 0;
23 : :
24 : 0 : lane = port->attr.nr_lane;
25 : 0 : rte_spinlock_lock(&port->rx_mac_lock);
26 : 0 : ctrl = RNP_MAC_REG_RD(hw, lane, RNP_MAC_RX_CFG);
27 [ # # ]: 0 : if (port->attr.link_ready) {
28 : 0 : ctrl &= ~RNP_MAC_LM;
29 : 0 : RNP_RX_ETH_ENABLE(hw, lane);
30 : : } else {
31 : 0 : RNP_RX_ETH_DISABLE(hw, lane);
32 : 0 : ctrl |= RNP_MAC_LM;
33 : : }
34 : 0 : RNP_MAC_REG_WR(hw, lane, RNP_MAC_RX_CFG, ctrl);
35 : : rte_spinlock_unlock(&port->rx_mac_lock);
36 : 0 : }
37 : :
38 : : static uint64_t
39 : : rnp_parse_speed_code(uint32_t speed_code)
40 : : {
41 : : uint32_t speed = 0;
42 : :
43 : : switch (speed_code) {
44 : : case RNP_LANE_SPEED_10M:
45 : : speed = RTE_ETH_SPEED_NUM_10M;
46 : : break;
47 : : case RNP_LANE_SPEED_100M:
48 : : speed = RTE_ETH_SPEED_NUM_100M;
49 : : break;
50 : : case RNP_LANE_SPEED_1G:
51 : : speed = RTE_ETH_SPEED_NUM_1G;
52 : : break;
53 : : case RNP_LANE_SPEED_10G:
54 : : speed = RTE_ETH_SPEED_NUM_10G;
55 : : break;
56 : : case RNP_LANE_SPEED_25G:
57 : : speed = RTE_ETH_SPEED_NUM_25G;
58 : : break;
59 : : case RNP_LANE_SPEED_40G:
60 : : speed = RTE_ETH_SPEED_NUM_40G;
61 : : break;
62 : : default:
63 : : speed = RTE_ETH_SPEED_NUM_UNKNOWN;
64 : : }
65 : :
66 : : return speed;
67 : : }
68 : :
69 : : static bool
70 : 0 : rnp_update_speed_changed(struct rnp_eth_port *port)
71 : : {
72 : 0 : struct rnp_hw *hw = port->hw;
73 : : uint32_t speed_code = 0;
74 : : bool speed_changed = 0;
75 : : bool duplex = false;
76 : : uint32_t magic = 0;
77 : : uint32_t linkstate;
78 : : uint64_t speed = 0;
79 : : uint16_t lane = 0;
80 : :
81 : 0 : lane = port->attr.nr_lane;
82 : 0 : linkstate = RNP_E_REG_RD(hw, RNP_DEVICE_LINK_EX);
83 : 0 : magic = linkstate & 0xF0000000;
84 : : /* check if speed is changed. even if link is not changed */
85 [ # # ]: 0 : if (RNP_SPEED_META_VALID(magic) &&
86 [ # # ]: 0 : (linkstate & RNP_LINK_STATE(lane))) {
87 : : speed_code = rnpce_link_speed_code(linkstate, lane);
88 : : speed = rnp_parse_speed_code(speed_code);
89 [ # # ]: 0 : if (speed != port->attr.speed) {
90 : 0 : duplex = RNP_LINK_DUPLEX_STATE(linkstate, lane);
91 : 0 : port->attr.phy_meta.link_duplex = duplex;
92 : 0 : port->attr.speed = speed;
93 : : speed_changed = 1;
94 : : }
95 : : }
96 : :
97 : 0 : return speed_changed;
98 : : }
99 : :
100 : : static bool
101 : 0 : rnp_update_link_changed(struct rnp_eth_port *port,
102 : : struct rnp_link_stat_req *link)
103 : : {
104 : 0 : uint16_t lane = port->attr.nr_lane;
105 : 0 : struct rnp_hw *hw = port->hw;
106 : : uint32_t link_up_bit = 0;
107 : : bool link_changed = 0;
108 : : uint32_t sync_bit = 0;
109 : : bool duplex = 0;
110 : :
111 : 0 : link_up_bit = link->lane_status & RTE_BIT32(lane);
112 : 0 : sync_bit = RNP_E_REG_RD(hw, RNP_FW_LINK_SYNC);
113 [ # # ]: 0 : if (link_up_bit) {
114 : : /* port link down to up */
115 [ # # ]: 0 : if (!port->attr.link_ready)
116 : : link_changed = true;
117 : 0 : port->attr.link_ready = true;
118 [ # # ]: 0 : if (link->port_st_magic == RNP_SPEED_VALID_MAGIC) {
119 : 0 : port->attr.speed = link->states[lane].speed;
120 : 0 : duplex = link->states[lane].duplex;
121 : 0 : port->attr.duplex = duplex;
122 : 0 : RNP_PMD_INFO("phy_id %d speed %d duplex "
123 : : "%d issgmii %d PortID %d",
124 : : link->states[lane].phy_addr,
125 : : link->states[lane].speed,
126 : : link->states[lane].duplex,
127 : : link->states[lane].is_sgmii,
128 : : port->attr.port_id);
129 : : }
130 : : } else {
131 : : /* port link to down */
132 [ # # ]: 0 : if (port->attr.link_ready)
133 : : link_changed = true;
134 : 0 : port->attr.link_ready = false;
135 : : }
136 [ # # ]: 0 : if (!link_changed && sync_bit != link_up_bit)
137 : : link_changed = 1;
138 : :
139 : 0 : return link_changed;
140 : : }
141 : :
142 : 0 : static void rnp_link_stat_sync_mark(struct rnp_hw *hw, int lane, int up)
143 : : {
144 : : uint32_t sync;
145 : :
146 : 0 : rte_spinlock_lock(&hw->link_sync);
147 : 0 : sync = RNP_E_REG_RD(hw, RNP_FW_LINK_SYNC);
148 : 0 : sync &= ~RNP_LINK_MAGIC_MASK;
149 : 0 : sync |= RNP_LINK_MAGIC_CODE;
150 [ # # ]: 0 : if (up)
151 : 0 : sync |= RTE_BIT32(lane);
152 : : else
153 : 0 : sync &= ~RTE_BIT32(lane);
154 : 0 : RNP_E_REG_WR(hw, RNP_FW_LINK_SYNC, sync);
155 : : rte_spinlock_unlock(&hw->link_sync);
156 : 0 : }
157 : :
158 : 0 : static void rnp_link_report(struct rnp_eth_port *port, bool link_en)
159 : : {
160 : 0 : struct rte_eth_dev_data *data = port->eth_dev->data;
161 : 0 : struct rnp_hw *hw = port->hw;
162 : : struct rnp_rx_queue *rxq;
163 : : struct rnp_tx_queue *txq;
164 : : struct rte_eth_link link;
165 : : uint16_t idx;
166 : :
167 [ # # ]: 0 : if (data == NULL)
168 : 0 : return;
169 [ # # ]: 0 : for (idx = 0; idx < data->nb_rx_queues; idx++) {
170 : 0 : rxq = data->rx_queues[idx];
171 [ # # ]: 0 : if (!rxq)
172 : 0 : continue;
173 : 0 : rxq->rx_link = link_en;
174 : : }
175 [ # # ]: 0 : for (idx = 0; idx < data->nb_tx_queues; idx++) {
176 : 0 : txq = data->tx_queues[idx];
177 [ # # ]: 0 : if (!txq)
178 : 0 : continue;
179 : 0 : txq->tx_link = link_en;
180 : : }
181 : : /* set default link info */
182 : 0 : link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
183 : 0 : link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
184 : 0 : link.link_status = RTE_ETH_LINK_DOWN;
185 : 0 : link.link_autoneg = RTE_ETH_LINK_FIXED;
186 [ # # ]: 0 : if (link_en) {
187 : 0 : link.link_autoneg = port->attr.phy_meta.link_autoneg;
188 : 0 : link.link_duplex = port->attr.phy_meta.link_duplex;
189 : 0 : link.link_speed = port->attr.speed;
190 : 0 : link.link_status = link_en;
191 : : }
192 : 0 : RNP_PMD_LOG(INFO, "PF[%d]link changed: changed_lane:0x%x, "
193 : : "status:0x%x",
194 : : hw->mbx.pf_num,
195 : : port->attr.nr_lane,
196 : : link_en);
197 : : /* report link info to upper firmwork */
198 : 0 : rte_eth_linkstatus_set(port->eth_dev, &link);
199 : : /* notice event process link status change */
200 : 0 : rte_eth_dev_callback_process(port->eth_dev, RTE_ETH_EVENT_INTR_LSC, NULL);
201 : : /* notice firmware LSC event sw received */
202 : 0 : rnp_link_stat_sync_mark(hw, port->attr.nr_lane, link_en);
203 : : }
204 : :
205 : 0 : static void rnp_dev_alarm_link_handler(void *param)
206 : : {
207 : : struct rnp_eth_port *port = param;
208 : : uint32_t status;
209 : :
210 [ # # # # ]: 0 : if (port == NULL || port->eth_dev == NULL)
211 : : return;
212 : 0 : status = port->attr.link_ready;
213 : 0 : rnp_link_report(port, status);
214 : : }
215 : :
216 : 0 : void rnp_link_event(struct rnp_eth_adapter *adapter,
217 : : struct rnp_mbx_fw_cmd_req *req)
218 : : {
219 : 0 : struct rnp_link_stat_req *link = (struct rnp_link_stat_req *)req->data;
220 : : struct rnp_hw *hw = &adapter->hw;
221 : : struct rnp_eth_port *port = NULL;
222 : : bool speed_changed;
223 : : bool link_changed;
224 : : uint32_t lane;
225 : : uint8_t i = 0;
226 : :
227 : : /* get real-time link && speed info */
228 [ # # ]: 0 : for (i = 0; i < hw->max_port_num; i++) {
229 : 0 : port = adapter->ports[i];
230 [ # # ]: 0 : if (port == NULL)
231 : 0 : continue;
232 : : speed_changed = false;
233 : : link_changed = false;
234 : 0 : lane = port->attr.nr_lane;
235 [ # # ]: 0 : if (RNP_LINK_NOCHANGED(lane, link->changed_lanes)) {
236 : 0 : speed_changed = rnp_update_speed_changed(port);
237 [ # # ]: 0 : if (!speed_changed)
238 : 0 : continue;
239 : : }
240 : 0 : link_changed = rnp_update_link_changed(port, link);
241 [ # # ]: 0 : if (link_changed || speed_changed) {
242 : 0 : rnp_link_flow_setup(port);
243 : 0 : rte_eal_alarm_set(RNP_ALARM_INTERVAL,
244 : : rnp_dev_alarm_link_handler,
245 : : (void *)port);
246 : : }
247 : : }
248 : 0 : }
249 : :
250 : 0 : int rnp_dev_link_update(struct rte_eth_dev *eth_dev,
251 : : int wait_to_complete)
252 : : {
253 : 0 : struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
254 : : struct rnp_phy_meta *phy_meta = &port->attr.phy_meta;
255 : 0 : uint16_t lane = port->attr.nr_lane;
256 : 0 : struct rnp_hw *hw = port->hw;
257 : : struct rte_eth_link link;
258 : : uint32_t status;
259 : :
260 : 0 : PMD_INIT_FUNC_TRACE();
261 : : memset(&link, 0, sizeof(link));
262 [ # # # # ]: 0 : if (wait_to_complete && rte_eal_process_type() == RTE_PROC_PRIMARY)
263 : 0 : rnp_mbx_fw_get_lane_stat(port);
264 : 0 : status = port->attr.link_ready;
265 : 0 : link.link_duplex = phy_meta->link_duplex;
266 : 0 : link.link_status = status ? RTE_ETH_LINK_UP : RTE_ETH_LINK_DOWN;
267 [ # # ]: 0 : if (link.link_status)
268 : 0 : link.link_speed = port->attr.speed;
269 : 0 : link.link_autoneg = phy_meta->link_autoneg ?
270 : 0 : RTE_ETH_LINK_AUTONEG : RTE_ETH_LINK_FIXED;
271 : 0 : rnp_link_stat_sync_mark(hw, lane, link.link_status);
272 : 0 : rte_eth_linkstatus_set(eth_dev, &link);
273 : :
274 : 0 : return 0;
275 : : }
276 : :
277 : 0 : static void rnp_dev_link_task(void *param)
278 : : {
279 : : struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
280 : 0 : struct rnp_eth_port *port = RNP_DEV_TO_PORT(dev);
281 : 0 : uint16_t lane = port->attr.nr_lane;
282 : 0 : struct rnp_hw *hw = port->hw;
283 : : bool speed_changed = false;
284 : : bool link_changed = false;
285 : : bool duplex_attr = false;
286 : : uint32_t speed_code = 0;
287 : : uint32_t link_state;
288 : : bool duplex = false;
289 : : uint32_t speed = 0;
290 : :
291 : 0 : link_state = RNP_E_REG_RD(hw, RNP_DEVICE_LINK_EX);
292 [ # # ]: 0 : if (link_state & RNP_LINK_DUPLEX_ATTR_EN)
293 : : duplex_attr = true;
294 : : else
295 : 0 : link_state = RNP_E_REG_RD(hw, RNP_DEVICE_LINK);
296 [ # # ]: 0 : if (link_state & RNP_LINK_STATE(lane)) {
297 : : /* Port link change to up */
298 : : speed_code = rnpce_link_speed_code(link_state, lane);
299 : : speed = rnp_parse_speed_code(speed_code);
300 [ # # ]: 0 : if (duplex_attr) {
301 : 0 : duplex = RNP_LINK_DUPLEX_STATE(link_state, lane);
302 : 0 : port->attr.phy_meta.link_duplex = duplex;
303 : : }
304 : 0 : port->attr.speed = speed;
305 : 0 : port->attr.pre_link = port->attr.link_ready;
306 : 0 : port->attr.link_ready = true;
307 : : } else {
308 : : /* Port link to down */
309 : 0 : port->attr.speed = RTE_ETH_SPEED_NUM_UNKNOWN;
310 : 0 : port->attr.pre_link = port->attr.link_ready;
311 : 0 : port->attr.link_ready = false;
312 : : }
313 [ # # ]: 0 : if (port->attr.pre_link != port->attr.link_ready)
314 : : link_changed = true;
315 : : if (!link_changed)
316 : 0 : speed_changed = rnp_update_speed_changed(port);
317 [ # # ]: 0 : if (link_changed || speed_changed) {
318 [ # # ]: 0 : if (!duplex_attr)
319 : 0 : rnp_mbx_fw_get_lane_stat(port);
320 : 0 : rnp_link_flow_setup(port);
321 : 0 : rnp_link_report(port, port->attr.link_ready);
322 : : }
323 : 0 : rte_eal_alarm_set(RNP_ALARM_INTERVAL,
324 : : rnp_dev_link_task,
325 : : (void *)dev);
326 : 0 : }
327 : :
328 : : void
329 : 0 : rnp_run_link_poll_task(struct rnp_eth_port *port)
330 : : {
331 : 0 : rte_eal_alarm_set(RNP_ALARM_INTERVAL, rnp_dev_link_task,
332 : 0 : (void *)port->eth_dev);
333 : 0 : }
334 : :
335 : : void
336 : 0 : rnp_cancel_link_poll_task(struct rnp_eth_port *port)
337 : : {
338 : 0 : rte_eal_alarm_cancel(rnp_dev_link_task, port->eth_dev);
339 : 0 : }
340 : :
341 : 0 : int rnp_dev_set_link_up(struct rte_eth_dev *eth_dev)
342 : : {
343 : 0 : struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
344 : 0 : uint16_t nr_lane = port->attr.nr_lane;
345 : 0 : struct rnp_hw *hw = port->hw;
346 : : struct rnp_rx_queue *rxq;
347 : : uint16_t timeout;
348 : : uint16_t index;
349 : : uint32_t state;
350 : : uint16_t idx;
351 : : int ret = 0;
352 : :
353 : 0 : PMD_INIT_FUNC_TRACE();
354 : :
355 [ # # ]: 0 : if (port->attr.link_ready)
356 : : return 0;
357 : : /* Cur link-state Is Down Verity The Rx Dma Queue State Is Empty */
358 [ # # ]: 0 : for (idx = 0; idx < eth_dev->data->nb_rx_queues; idx++) {
359 : 0 : rxq = eth_dev->data->rx_queues[idx];
360 [ # # ]: 0 : if (!rxq)
361 : 0 : continue;
362 : 0 : index = rxq->attr.index;
363 : : timeout = 0;
364 : : do {
365 [ # # ]: 0 : if (!RNP_E_REG_RD(hw, RNP_RXQ_READY(index)))
366 : : break;
367 : 0 : rte_delay_us(10);
368 : 0 : timeout++;
369 [ # # ]: 0 : } while (timeout < 1000);
370 : : }
371 : 0 : ret = rnp_mbx_fw_ifup_down(port, TRUE);
372 [ # # ]: 0 : if (ret) {
373 : 0 : RNP_PMD_WARN("port[%d] is set linkup failed",
374 : : eth_dev->data->port_id);
375 : 0 : return ret;
376 : : }
377 : : timeout = 0;
378 : : do {
379 : 0 : rte_io_rmb();
380 : 0 : state = RNP_E_REG_RD(hw, RNP_FW_LINK_SYNC);
381 [ # # ]: 0 : if (state & RTE_BIT32(nr_lane))
382 : : break;
383 : 0 : timeout++;
384 : 0 : rte_delay_us(10);
385 [ # # ]: 0 : } while (timeout < 100);
386 : :
387 : : return ret;
388 : : }
389 : :
390 : 0 : int rnp_dev_set_link_down(struct rte_eth_dev *eth_dev)
391 : : {
392 : 0 : struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
393 : 0 : uint16_t nr_lane = port->attr.nr_lane;
394 : 0 : struct rnp_hw *hw = port->hw;
395 : : struct rnp_tx_queue *txq;
396 : : uint32_t timeout = 0;
397 : : uint32_t check_v;
398 : : uint32_t state;
399 : : uint16_t idx;
400 : :
401 : 0 : PMD_INIT_FUNC_TRACE();
402 : 0 : RNP_RX_ETH_DISABLE(hw, nr_lane);
403 [ # # ]: 0 : for (idx = 0; idx < eth_dev->data->nb_tx_queues; idx++) {
404 : 0 : txq = eth_dev->data->tx_queues[idx];
405 [ # # ]: 0 : if (!txq)
406 : 0 : continue;
407 : 0 : txq->tx_link = false;
408 : : }
409 : : /* 2 Check eth tx fifo empty state */
410 : : do {
411 : 0 : state = RNP_E_REG_RD(hw, RNP_ETH_TX_FIFO_STATE);
412 : 0 : check_v = RNP_ETH_TX_FIFO_EMPT(nr_lane);
413 : 0 : state &= check_v;
414 [ # # ]: 0 : if (state == check_v)
415 : : break;
416 : 0 : rte_delay_us(10);
417 : 0 : timeout++;
418 [ # # ]: 0 : if (timeout >= 1000) {
419 : 0 : RNP_PMD_WARN("lane[%d] isn't empty of link-down action",
420 : : nr_lane);
421 : 0 : break;
422 : : }
423 : : } while (1);
424 : : /* 3 Tell Firmware Do Link-down Event Work */
425 : 0 : rnp_mbx_fw_ifup_down(port, FALSE);
426 : : /* 4 Wait For Link-Down that Firmware Do done */
427 : : timeout = 0;
428 : : do {
429 [ # # ]: 0 : if (!port->attr.link_ready)
430 : : break;
431 : 0 : rte_delay_us(10);
432 : 0 : timeout++;
433 [ # # ]: 0 : } while (timeout < 2000);
434 : :
435 : 0 : return 0;
436 : : }
|