blob: 8d9388983b79c1415f0c299ce754bfbe601544c5 [file] [log] [blame]
Aaron Williamsdc072772022-04-07 09:11:13 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018-2022 Marvell International Ltd.
4 *
5 * Helper functions to abstract board specific data about
6 * network ports from the rest of the cvmx-helper files.
7 */
8
9#include <i2c.h>
10#include <log.h>
11#include <malloc.h>
12#include <net.h>
13#include <linux/delay.h>
14
15#include <mach/cvmx-regs.h>
16#include <mach/cvmx-csr.h>
17#include <mach/cvmx-bootmem.h>
18#include <mach/octeon-model.h>
19#include <mach/octeon_fdt.h>
20#include <mach/cvmx-helper.h>
21#include <mach/cvmx-helper-board.h>
22#include <mach/cvmx-helper-cfg.h>
23#include <mach/cvmx-helper-fdt.h>
24#include <mach/cvmx-helper-gpio.h>
25
26#include <mach/cvmx-smix-defs.h>
27#include <mach/cvmx-mdio.h>
28#include <mach/cvmx-qlm.h>
29
30DECLARE_GLOBAL_DATA_PTR;
31
32static bool sfp_parsed;
33
34static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info,
35 int ipd_port);
36static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port);
37
38/**
39 * Writes to a Microsemi VSC7224 16-bit register
40 *
41 * @param[in] i2c_bus i2c bus data structure (must be enabled)
42 * @param addr Address of VSC7224 on the i2c bus
43 * @param reg 8-bit register number to write to
44 * @param val 16-bit value to write
45 *
46 * @return 0 for success
47 */
48static int cvmx_write_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus,
49 u8 addr, u8 reg, u16 val)
50{
51 struct udevice *dev;
52 u8 buffer[2];
53 int ret;
54
55 ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev);
56 if (ret) {
57 debug("Cannot find I2C device: %d\n", ret);
58 return -1;
59 }
60
61 ret = dm_i2c_write(dev, reg, buffer, 2);
62 if (ret) {
63 debug("Cannot write I2C device: %d\n", ret);
64 return -1;
65 }
66
67 return 0;
68}
69
70/**
71 * Writes to a Microsemi VSC7224 16-bit register
72 *
73 * @param[in] i2c_bus i2c bus data structure (must be enabled)
74 * @param addr Address of VSC7224 on the i2c bus
75 * @param reg 8-bit register number to write to
76 *
77 * @return 16-bit value or error if < 0
78 */
79static int cvmx_read_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus,
80 u8 addr, u8 reg)
81{
82 struct udevice *dev;
83 u8 buffer[2];
84 int ret;
85
86 ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev);
87 if (ret) {
88 debug("Cannot find I2C device: %d\n", ret);
89 return -1;
90 }
91
92 ret = dm_i2c_read(dev, reg, buffer, 2);
93 if (ret) {
94 debug("Cannot read I2C device: %d\n", ret);
95 return -1;
96 }
97
98 return (buffer[0] << 8) | buffer[1];
99}
100
101/**
102 * Function called whenever mod_abs/mod_prs has changed for Microsemi VSC7224
103 *
104 * @param sfp pointer to SFP data structure
105 * @param val 1 if absent, 0 if present, otherwise not set
106 * @param data user-defined data
107 *
108 * @return 0 for success, -1 on error
109 */
110int cvmx_sfp_vsc7224_mod_abs_changed(struct cvmx_fdt_sfp_info *sfp, int val,
111 void *data)
112{
113 int err;
114 struct cvmx_sfp_mod_info *mod_info;
115 int length;
116 struct cvmx_vsc7224 *vsc7224;
117 struct cvmx_vsc7224_chan *vsc7224_chan;
118 struct cvmx_vsc7224_tap *taps, *match = NULL;
119 int i;
120
121 debug("%s(%s, %d, %p): Module %s\n", __func__, sfp->name, val, data,
122 val ? "absent" : "present");
123 if (val)
124 return 0;
125
126 /* We're here if we detect that the module is now present */
127 err = cvmx_sfp_read_i2c_eeprom(sfp);
128 if (err) {
129 debug("%s: Error reading the SFP module eeprom for %s\n",
130 __func__, sfp->name);
131 return err;
132 }
133 mod_info = &sfp->sfp_info;
134
135 if (!mod_info->valid || !sfp->valid) {
136 debug("%s: Module data is invalid\n", __func__);
137 return -1;
138 }
139
140 vsc7224_chan = sfp->vsc7224_chan;
141 while (vsc7224_chan) {
142 /* We don't do any rx tuning */
143 if (!vsc7224_chan->is_tx) {
144 vsc7224_chan = vsc7224_chan->next;
145 continue;
146 }
147
148 /* Walk through all the channels */
149 taps = vsc7224_chan->taps;
150 if (mod_info->limiting)
151 length = 0;
152 else
153 length = mod_info->max_copper_cable_len;
154 debug("%s: limiting: %d, length: %d\n", __func__,
155 mod_info->limiting, length);
156
157 /* Find a matching length in the taps table */
158 for (i = 0; i < vsc7224_chan->num_taps; i++) {
159 if (length >= taps->len)
160 match = taps;
161 taps++;
162 }
163 if (!match) {
164 debug("%s(%s, %d, %p): Error: no matching tap for length %d\n",
165 __func__, sfp->name, val, data, length);
166 return -1;
167 }
168 debug("%s(%s): Applying %cx taps to vsc7224 %s:%d for cable length %d+\n",
169 __func__, sfp->name, vsc7224_chan->is_tx ? 't' : 'r',
170 vsc7224_chan->vsc7224->name, vsc7224_chan->lane,
171 match->len);
172 /* Program the taps */
173 vsc7224 = vsc7224_chan->vsc7224;
174 cvmx_write_vsc7224_reg(vsc7224->i2c_bus, vsc7224->i2c_addr,
175 0x7f, vsc7224_chan->lane);
176 if (!vsc7224_chan->maintap_disable)
177 cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
178 vsc7224->i2c_addr, 0x99,
179 match->main_tap);
180 if (!vsc7224_chan->pretap_disable)
181 cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
182 vsc7224->i2c_addr, 0x9a,
183 match->pre_tap);
184 if (!vsc7224_chan->posttap_disable)
185 cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
186 vsc7224->i2c_addr, 0x9b,
187 match->post_tap);
188
189 /* Re-use val and disable taps if needed */
190 if (vsc7224_chan->maintap_disable ||
191 vsc7224_chan->pretap_disable ||
192 vsc7224_chan->posttap_disable) {
193 val = cvmx_read_vsc7224_reg(vsc7224->i2c_bus,
194 vsc7224->i2c_addr, 0x97);
195 if (vsc7224_chan->maintap_disable)
196 val |= 0x800;
197 if (vsc7224_chan->pretap_disable)
198 val |= 0x1000;
199 if (vsc7224_chan->posttap_disable)
200 val |= 0x400;
201 cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
202 vsc7224->i2c_addr, 0x97, val);
203 }
204 vsc7224_chan = vsc7224_chan->next;
205 }
206
207 return err;
208}
209
210/**
211 * Update the mod_abs and error LED
212 *
213 * @param ipd_port ipd port number
214 * @param link link information
215 */
216static void __cvmx_helper_update_sfp(int ipd_port,
217 struct cvmx_fdt_sfp_info *sfp_info,
218 cvmx_helper_link_info_t link)
219{
220 debug("%s(%d): checking mod_abs\n", __func__, ipd_port);
221
222 cvmx_sfp_check_mod_abs(sfp_info, sfp_info->mod_abs_data);
223}
224
225static void cvmx_sfp_update_link(struct cvmx_fdt_sfp_info *sfp,
226 cvmx_helper_link_info_t link)
227{
228 while (sfp) {
229 debug("%s(%s): checking mod_abs\n", __func__, sfp->name);
230 if (link.s.link_up && sfp->last_mod_abs)
231 cvmx_sfp_check_mod_abs(sfp, sfp->mod_abs_data);
232 sfp = sfp->next_iface_sfp;
233 }
234}
235
236/**
237 * @INTERNAL
238 * This function is used ethernet ports link speed. This functions uses the
239 * device tree information to determine the phy address and type of PHY.
240 * The only supproted PHYs are Marvell and Broadcom.
241 *
242 * @param ipd_port IPD input port associated with the port we want to get link
243 * status for.
244 *
245 * @return The ports link status. If the link isn't fully resolved, this must
246 * return zero.
247 */
248cvmx_helper_link_info_t __cvmx_helper_board_link_get_from_dt(int ipd_port)
249{
250 cvmx_helper_link_info_t result;
251 cvmx_phy_info_t *phy_info = NULL;
252 cvmx_phy_info_t local_phy_info;
253 int xiface = 0, index = 0;
254 bool use_inband = false;
255 struct cvmx_fdt_sfp_info *sfp_info;
256 const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
257
258 result.u64 = 0;
259
260 if (ipd_port >= 0) {
261 int mode;
262
263 xiface = cvmx_helper_get_interface_num(ipd_port);
264 index = cvmx_helper_get_interface_index_num(ipd_port);
265 mode = cvmx_helper_interface_get_mode(xiface);
266 if (!cvmx_helper_get_port_autonegotiation(xiface, index)) {
267 result.s.link_up = 1;
268 result.s.full_duplex = 1;
269 switch (mode) {
270 case CVMX_HELPER_INTERFACE_MODE_RGMII:
271 case CVMX_HELPER_INTERFACE_MODE_GMII:
272 case CVMX_HELPER_INTERFACE_MODE_SGMII:
273 case CVMX_HELPER_INTERFACE_MODE_QSGMII:
274 case CVMX_HELPER_INTERFACE_MODE_AGL:
275 case CVMX_HELPER_INTERFACE_MODE_SPI:
276 if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
277 struct cvmx_xiface xi =
278 cvmx_helper_xiface_to_node_interface(
279 xiface);
280 u64 gbaud = cvmx_qlm_get_gbaud_mhz(0);
281
282 result.s.speed = gbaud * 8 / 10;
283 if (cvmx_qlm_get_dlm_mode(
284 0, xi.interface) ==
285 CVMX_QLM_MODE_SGMII)
286 result.s.speed >>= 1;
287 else
288 result.s.speed >>= 2;
289 } else {
290 result.s.speed = 1000;
291 }
292 break;
293 case CVMX_HELPER_INTERFACE_MODE_RXAUI:
294 case CVMX_HELPER_INTERFACE_MODE_XAUI:
295 case CVMX_HELPER_INTERFACE_MODE_10G_KR:
296 case CVMX_HELPER_INTERFACE_MODE_XFI:
297 result.s.speed = 10000;
298 break;
299 case CVMX_HELPER_INTERFACE_MODE_XLAUI:
300 case CVMX_HELPER_INTERFACE_MODE_40G_KR4:
301 result.s.speed = 40000;
302 break;
303 default:
304 break;
305 }
306
307 sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index);
308 /* Initialize the SFP info if it hasn't already been
309 * done.
310 */
311 if (!sfp_info && !sfp_parsed) {
312 cvmx_sfp_parse_device_tree(fdt_addr);
313 sfp_parsed = true;
314 cvmx_sfp_read_all_modules();
315 sfp_info = cvmx_helper_cfg_get_sfp_info(xiface,
316 index);
317 }
318 /* If the link is down or the link is up but we still
319 * register the module as being absent, re-check
320 * mod_abs.
321 */
322 cvmx_sfp_update_link(sfp_info, result);
323
324 cvmx_helper_update_link_led(xiface, index, result);
325
326 return result;
327 }
328 phy_info = cvmx_helper_get_port_phy_info(xiface, index);
329 if (!phy_info) {
330 debug("%s: phy info not saved in config, allocating for 0x%x:%d\n",
331 __func__, xiface, index);
332
333 phy_info = (cvmx_phy_info_t *)cvmx_bootmem_alloc(
334 sizeof(*phy_info), 0);
335 if (!phy_info) {
336 debug("%s: Out of memory\n", __func__);
337 return result;
338 }
339 memset(phy_info, 0, sizeof(*phy_info));
340 phy_info->phy_addr = -1;
341 debug("%s: Setting phy info for 0x%x:%d to %p\n",
342 __func__, xiface, index, phy_info);
343 cvmx_helper_set_port_phy_info(xiface, index, phy_info);
344 }
345 } else {
346 /* For management ports we don't store the PHY information
347 * so we use a local copy instead.
348 */
349 phy_info = &local_phy_info;
350 memset(phy_info, 0, sizeof(*phy_info));
351 phy_info->phy_addr = -1;
352 }
353
354 if (phy_info->phy_addr == -1) {
355 if (octeon_has_feature(OCTEON_FEATURE_BGX)) {
356 if (__cvmx_helper_78xx_parse_phy(phy_info, ipd_port)) {
357 phy_info->phy_addr = -1;
358 use_inband = true;
359 }
360 } else if (__get_phy_info_from_dt(phy_info, ipd_port) < 0) {
361 phy_info->phy_addr = -1;
362 use_inband = true;
363 }
364 }
365
366 /* If we can't get the PHY info from the device tree then try
367 * the inband state.
368 */
369 if (use_inband) {
370 result.s.full_duplex = 1;
371 result.s.link_up = 1;
372 result.s.speed = 1000;
373 return result;
374 }
375
376 if (phy_info->phy_addr < 0)
377 return result;
378
379 if (phy_info->link_function)
380 result = phy_info->link_function(phy_info);
381 else
382 result = cvmx_helper_link_get(ipd_port);
383
384 sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index);
385 while (sfp_info) {
386 /* If the link is down or the link is up but we still register
387 * the module as being absent, re-check mod_abs.
388 */
Bryan Brattlof5f4bc5d2025-04-09 12:26:20 -0500389 if (!result.s.link_up || sfp_info->last_mod_abs)
Aaron Williamsdc072772022-04-07 09:11:13 +0200390 __cvmx_helper_update_sfp(ipd_port, sfp_info, result);
391 sfp_info = sfp_info->next_iface_sfp;
392 }
393
394 return result;
395}
396
397cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
398{
399 cvmx_helper_link_info_t result;
400
401 /* Unless we fix it later, all links are defaulted to down */
402 result.u64 = 0;
403
404 return __cvmx_helper_board_link_get_from_dt(ipd_port);
405}
406
407void cvmx_helper_update_link_led(int xiface, int index,
408 cvmx_helper_link_info_t result)
409{
410}
411
412void cvmx_helper_leds_show_error(struct cvmx_phy_gpio_leds *leds, bool error)
413{
414}
415
416int __cvmx_helper_board_interface_probe(int interface, int supported_ports)
417{
418 return supported_ports;
419}
420
421/**
422 * Returns the Ethernet node offset in the device tree
423 *
424 * @param fdt_addr - pointer to flat device tree in memory
425 * @param aliases - offset of alias in device tree
426 * @param ipd_port - ipd port number to look up
427 *
428 * @returns offset of Ethernet node if >= 0, error if -1
429 */
430int __pip_eth_node(const void *fdt_addr, int aliases, int ipd_port)
431{
432 char name_buffer[20];
433 const char *pip_path;
434 int pip, iface, eth;
435 int interface_num = cvmx_helper_get_interface_num(ipd_port);
436 int interface_index = cvmx_helper_get_interface_index_num(ipd_port);
437 cvmx_helper_interface_mode_t interface_mode =
438 cvmx_helper_interface_get_mode(interface_num);
439
440 /* The following are not found in the device tree */
441 switch (interface_mode) {
442 case CVMX_HELPER_INTERFACE_MODE_ILK:
443 case CVMX_HELPER_INTERFACE_MODE_LOOP:
444 case CVMX_HELPER_INTERFACE_MODE_SRIO:
445 debug("ERROR: No node expected for interface: %d, port: %d, mode: %s\n",
446 interface_index, ipd_port,
447 cvmx_helper_interface_mode_to_string(interface_mode));
448 return -1;
449 default:
450 break;
451 }
452 pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL);
453 if (!pip_path) {
454 debug("ERROR: pip path not found in device tree\n");
455 return -1;
456 }
457 pip = fdt_path_offset(fdt_addr, pip_path);
458 debug("ipdd_port=%d pip_path=%s pip=%d ", ipd_port, pip_path, pip);
459 if (pip < 0) {
460 debug("ERROR: pip not found in device tree\n");
461 return -1;
462 }
463 snprintf(name_buffer, sizeof(name_buffer), "interface@%d",
464 interface_num);
465 iface = fdt_subnode_offset(fdt_addr, pip, name_buffer);
466 debug("iface=%d ", iface);
467 if (iface < 0) {
468 debug("ERROR : pip intf %d not found in device tree\n",
469 interface_num);
470 return -1;
471 }
472 snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x",
473 interface_index);
474 eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
475 debug("eth=%d\n", eth);
476 if (eth < 0) {
477 debug("ERROR : pip interface@%d ethernet@%d not found in device tree\n",
478 interface_num, interface_index);
479 return -1;
480 }
481 return eth;
482}
483
484int __mix_eth_node(const void *fdt_addr, int aliases, int interface_index)
485{
486 char name_buffer[20];
487 const char *mix_path;
488 int mix;
489
490 snprintf(name_buffer, sizeof(name_buffer), "mix%d", interface_index);
491 mix_path =
492 (const char *)fdt_getprop(fdt_addr, aliases, name_buffer, NULL);
493 if (!mix_path) {
494 debug("ERROR: mix%d path not found in device tree\n",
495 interface_index);
496 }
497 mix = fdt_path_offset(fdt_addr, mix_path);
498 if (mix < 0) {
499 debug("ERROR: %s not found in device tree\n", mix_path);
500 return -1;
501 }
502 return mix;
503}
504
505static int __mdiobus_addr_to_unit(u32 addr)
506{
507 int unit = (addr >> 7) & 3;
508
509 if (!OCTEON_IS_MODEL(OCTEON_CN68XX) && !OCTEON_IS_MODEL(OCTEON_CN78XX))
510 unit >>= 1;
511 return unit;
512}
513
514/**
515 * Parse the muxed MDIO interface information from the device tree
516 *
517 * @param phy_info - pointer to phy info data structure to update
518 * @param mdio_offset - offset of MDIO bus
519 * @param mux_offset - offset of MUX, parent of mdio_offset
520 *
521 * @return 0 for success or -1
522 */
523static int __get_muxed_mdio_info_from_dt(cvmx_phy_info_t *phy_info,
524 int mdio_offset, int mux_offset)
525{
526 const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
527 int phandle;
528 int smi_offset;
529 int gpio_offset;
530 u64 smi_addr = 0;
531 int len;
532 u32 *pgpio_handle;
533 int gpio_count = 0;
534 u32 *prop_val;
535 int offset;
536 const char *prop_name;
537
538 debug("%s(%p, 0x%x, 0x%x)\n", __func__, phy_info, mdio_offset,
539 mux_offset);
540
541 /* Get register value to put onto the GPIO lines to select */
542 phy_info->gpio_value =
543 cvmx_fdt_get_int(fdt_addr, mdio_offset, "reg", -1);
544 if (phy_info->gpio_value < 0) {
545 debug("Could not get register value for muxed MDIO bus from DT\n");
546 return -1;
547 }
548
549 smi_offset = cvmx_fdt_lookup_phandle(fdt_addr, mux_offset,
550 "mdio-parent-bus");
551 if (smi_offset < 0) {
552 debug("Invalid SMI offset for muxed MDIO interface in device tree\n");
553 return -1;
554 }
555 smi_addr = cvmx_fdt_get_uint64(fdt_addr, smi_offset, "reg", 0);
556
557 /* Convert SMI address to a MDIO interface */
558 switch (smi_addr) {
559 case 0x1180000001800:
560 case 0x1180000003800: /* 68XX address */
561 phy_info->mdio_unit = 0;
562 break;
563 case 0x1180000001900:
564 case 0x1180000003880:
565 phy_info->mdio_unit = 1;
566 break;
567 case 0x1180000003900:
568 phy_info->mdio_unit = 2;
569 break;
570 case 0x1180000003980:
571 phy_info->mdio_unit = 3;
572 break;
573 default:
574 phy_info->mdio_unit = 1;
575 break;
576 }
577 /* Find the GPIO MUX controller */
578 pgpio_handle =
579 (u32 *)fdt_getprop(fdt_addr, mux_offset, "gpios", &len);
580 if (!pgpio_handle || len < 12 || (len % 12) != 0 ||
581 len > CVMX_PHY_MUX_MAX_GPIO * 12) {
582 debug("Invalid GPIO for muxed MDIO controller in DT\n");
583 return -1;
584 }
585
586 for (gpio_count = 0; gpio_count < len / 12; gpio_count++) {
587 phandle = fdt32_to_cpu(pgpio_handle[gpio_count * 3]);
588 phy_info->gpio[gpio_count] =
589 fdt32_to_cpu(pgpio_handle[gpio_count * 3 + 1]);
590 gpio_offset = fdt_node_offset_by_phandle(fdt_addr, phandle);
591 if (gpio_offset < 0) {
592 debug("Cannot access parent GPIO node in DT\n");
593 return -1;
594 }
595 if (!fdt_node_check_compatible(fdt_addr, gpio_offset,
596 "cavium,octeon-3860-gpio")) {
597 phy_info->gpio_type[gpio_count] = GPIO_OCTEON;
598 } else if (!fdt_node_check_compatible(fdt_addr, gpio_offset,
599 "nxp,pca8574")) {
600 /* GPIO is a TWSI GPIO unit which might sit behind
601 * another mux.
602 */
603 phy_info->gpio_type[gpio_count] = GPIO_PCA8574;
604 prop_val = (u32 *)fdt_getprop(
605 fdt_addr, gpio_offset, "reg", NULL);
606 if (!prop_val) {
607 debug("Could not find TWSI address of npx pca8574 GPIO from DT\n");
608 return -1;
609 }
610 /* Get the TWSI address of the GPIO unit */
611 phy_info->cvmx_gpio_twsi[gpio_count] =
612 fdt32_to_cpu(*prop_val);
613 /* Get the selector on the GPIO mux if present */
614 offset = fdt_parent_offset(fdt_addr, gpio_offset);
615 prop_val = (u32 *)fdt_getprop(fdt_addr, offset,
616 "reg", NULL);
617 if (prop_val) {
618 phy_info->gpio_parent_mux_select =
619 fdt32_to_cpu(*prop_val);
620 /* Go up another level */
621 offset = fdt_parent_offset(fdt_addr, offset);
622 if (!fdt_node_check_compatible(fdt_addr, offset,
623 "nxp,pca9548")) {
624 prop_val = (u32 *)fdt_getprop(
625 fdt_addr, offset, "reg", NULL);
626 if (!prop_val) {
627 debug("Could not read MUX TWSI address from DT\n");
628 return -1;
629 }
630 phy_info->gpio_parent_mux_twsi =
631 fdt32_to_cpu(*prop_val);
632 }
633 }
634 } else {
635 prop_name = (char *)fdt_getprop(fdt_addr, gpio_offset,
636 "compatible", NULL);
637 debug("Unknown GPIO type %s\n", prop_name);
638 return -1;
639 }
640 }
641 return 0;
642}
643
644/**
645 * @INTERNAL
646 * Converts a BGX address to the node, interface and port number
647 *
648 * @param bgx_addr Address of CSR register
649 *
650 * @return node, interface and port number, will be -1 for invalid address.
651 */
652static struct cvmx_xiface __cvmx_bgx_reg_addr_to_xiface(u64 bgx_addr)
653{
654 struct cvmx_xiface xi = { -1, -1 };
655
656 xi.node = cvmx_csr_addr_to_node(bgx_addr);
657 bgx_addr = cvmx_csr_addr_strip_node(bgx_addr);
658 if ((bgx_addr & 0xFFFFFFFFF0000000) != 0x00011800E0000000) {
659 debug("%s: Invalid BGX address 0x%llx\n", __func__,
660 (unsigned long long)bgx_addr);
661 xi.node = -1;
662 return xi;
663 }
664 xi.interface = (bgx_addr >> 24) & 0x0F;
665
666 return xi;
667}
668
669static cvmx_helper_link_info_t
670__get_marvell_phy_link_state(cvmx_phy_info_t *phy_info)
671{
672 cvmx_helper_link_info_t result;
673 int phy_status;
674 u32 phy_addr = phy_info->phy_addr;
675
676 result.u64 = 0;
677 /* Set to page 0 */
678 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, 22, 0);
679 /* All the speed information can be read from register 17 in one go. */
680 phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
681
682 /* If the resolve bit 11 isn't set, see if autoneg is turned off
683 * (bit 12, reg 0). The resolve bit doesn't get set properly when
684 * autoneg is off, so force it
685 */
686 if ((phy_status & (1 << 11)) == 0) {
687 int auto_status =
688 cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
689 if ((auto_status & (1 << 12)) == 0)
690 phy_status |= 1 << 11;
691 }
692
693 /* Link is up = Speed/Duplex Resolved + RT-Link Up + G-Link Up. */
694 if ((phy_status & 0x0c08) == 0x0c08) {
695 result.s.link_up = 1;
696 result.s.full_duplex = ((phy_status >> 13) & 1);
697 switch ((phy_status >> 14) & 3) {
698 case 0: /* 10 Mbps */
699 result.s.speed = 10;
700 break;
701 case 1: /* 100 Mbps */
702 result.s.speed = 100;
703 break;
704 case 2: /* 1 Gbps */
705 result.s.speed = 1000;
706 break;
707 case 3: /* Illegal */
708 result.u64 = 0;
709 break;
710 }
711 }
712 return result;
713}
714
715/**
716 * @INTERNAL
717 * Get link state of broadcom PHY
718 *
719 * @param phy_info PHY information
720 */
721static cvmx_helper_link_info_t
722__get_broadcom_phy_link_state(cvmx_phy_info_t *phy_info)
723{
724 cvmx_helper_link_info_t result;
725 u32 phy_addr = phy_info->phy_addr;
726 int phy_status;
727
728 result.u64 = 0;
729 /* Below we are going to read SMI/MDIO register 0x19 which works
730 * on Broadcom parts
731 */
732 phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x19);
733 switch ((phy_status >> 8) & 0x7) {
734 case 0:
735 result.u64 = 0;
736 break;
737 case 1:
738 result.s.link_up = 1;
739 result.s.full_duplex = 0;
740 result.s.speed = 10;
741 break;
742 case 2:
743 result.s.link_up = 1;
744 result.s.full_duplex = 1;
745 result.s.speed = 10;
746 break;
747 case 3:
748 result.s.link_up = 1;
749 result.s.full_duplex = 0;
750 result.s.speed = 100;
751 break;
752 case 4:
753 result.s.link_up = 1;
754 result.s.full_duplex = 1;
755 result.s.speed = 100;
756 break;
757 case 5:
758 result.s.link_up = 1;
759 result.s.full_duplex = 1;
760 result.s.speed = 100;
761 break;
762 case 6:
763 result.s.link_up = 1;
764 result.s.full_duplex = 0;
765 result.s.speed = 1000;
766 break;
767 case 7:
768 result.s.link_up = 1;
769 result.s.full_duplex = 1;
770 result.s.speed = 1000;
771 break;
772 }
773 return result;
774}
775
776/**
777 * @INTERNAL
778 * Get link state of generic gigabit PHY
779 *
780 * @param phy_info - information about the PHY
781 *
782 * @returns link status of the PHY
783 */
784static cvmx_helper_link_info_t
785__cvmx_get_generic_8023_c22_phy_link_state(cvmx_phy_info_t *phy_info)
786{
787 cvmx_helper_link_info_t result;
788 u32 phy_addr = phy_info->phy_addr;
789 int phy_basic_control; /* Register 0x0 */
790 int phy_basic_status; /* Register 0x1 */
791 int phy_anog_adv; /* Register 0x4 */
792 int phy_link_part_avail; /* Register 0x5 */
793 int phy_control; /* Register 0x9 */
794 int phy_status; /* Register 0xA */
795
796 result.u64 = 0;
797
798 phy_basic_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 1);
799 if (!(phy_basic_status & 0x4)) /* Check if link is up */
800 return result; /* Link is down, return link down */
801
802 result.s.link_up = 1;
803 phy_basic_control = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
804 /* Check if autonegotiation is enabled and completed */
805 if ((phy_basic_control & (1 << 12)) && (phy_basic_status & (1 << 5))) {
806 phy_status =
807 cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0xA);
808 phy_control =
809 cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x9);
810
811 phy_status &= phy_control << 2;
812 phy_link_part_avail =
813 cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x5);
814 phy_anog_adv =
815 cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x4);
816 phy_link_part_avail &= phy_anog_adv;
817
818 if (phy_status & 0xC00) { /* Gigabit full or half */
819 result.s.speed = 1000;
820 result.s.full_duplex = !!(phy_status & 0x800);
821 } else if (phy_link_part_avail &
822 0x0180) { /* 100 full or half */
823 result.s.speed = 100;
824 result.s.full_duplex = !!(phy_link_part_avail & 0x100);
825 } else if (phy_link_part_avail & 0x0060) {
826 result.s.speed = 10;
827 result.s.full_duplex = !!(phy_link_part_avail & 0x0040);
828 }
829 } else {
830 /* Not autonegotiated */
831 result.s.full_duplex = !!(phy_basic_control & (1 << 8));
832
833 if (phy_basic_control & (1 << 6))
834 result.s.speed = 1000;
835 else if (phy_basic_control & (1 << 13))
836 result.s.speed = 100;
837 else
838 result.s.speed = 10;
839 }
840 return result;
841}
842
843static cvmx_helper_link_info_t
844__cvmx_get_qualcomm_s17_phy_link_state(cvmx_phy_info_t *phy_info)
845{
846 cvmx_helper_link_info_t result;
847 u32 phy_addr = phy_info->phy_addr;
848 int phy_status;
849 int auto_status;
850
851 result.u64 = 0;
852
853 phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
854
855 /* If bit 11 isn't set see if autonegotiation is turned off
856 * (bit 12, reg 0). The resolved bit doesn't get set properly when
857 * autonegotiation is off, so force it.
858 */
859 if ((phy_status & (1 << 11)) == 0) {
860 auto_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
861 if ((auto_status & (1 << 12)) == 0)
862 phy_status |= 1 << 11;
863 }
864 /* Only return a link if the PHY has finished autonegotiation and set
865 * the resolved bit (bit 11).
866 */
867 if (phy_status & (1 << 11)) {
868 result.s.link_up = 1;
869 result.s.full_duplex = !!(phy_status & (1 << 13));
870 switch ((phy_status >> 14) & 3) {
871 case 0: /* 10Mbps */
872 result.s.speed = 10;
873 break;
874 case 1: /* 100Mbps */
875 result.s.speed = 100;
876 break;
877 case 2: /* 1Gbps */
878 result.s.speed = 1000;
879 break;
880 default: /* Illegal */
881 result.u64 = 0;
882 break;
883 }
884 }
885 debug(" link: %s, duplex: %s, speed: %lu\n",
886 result.s.link_up ? "up" : "down",
887 result.s.full_duplex ? "full" : "half",
888 (unsigned long)result.s.speed);
889 return result;
890}
891
892static cvmx_helper_link_info_t
893__get_generic_8023_c45_phy_link_state(cvmx_phy_info_t *phy_info)
894{
895 cvmx_helper_link_info_t result;
896 int phy_status;
897 int pma_ctrl1;
898 u32 phy_addr = phy_info->phy_addr;
899
900 result.u64 = 0;
901 pma_ctrl1 = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0);
902 if ((pma_ctrl1 & 0x207c) == 0x2040)
903 result.s.speed = 10000;
904 /* PMA Status 1 (1x0001) */
905 phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0xa);
906 if (phy_status < 0)
907 return result;
908
909 result.s.full_duplex = 1;
910 if ((phy_status & 1) == 0)
911 return result;
912 phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 4, 0x18);
913 if (phy_status < 0)
914 return result;
915 result.s.link_up = (phy_status & 0x1000) ? 1 : 0;
916
917 return result;
918}
919
920static cvmx_helper_link_info_t
921__cvmx_get_cortina_phy_link_state(cvmx_phy_info_t *phy_info)
922{
923 cvmx_helper_link_info_t result;
924
925 result.s.link_up = 1;
926 result.s.full_duplex = 1;
927 result.s.speed = 1000;
928 return result;
929}
930
931static cvmx_helper_link_info_t
932__get_vitesse_vsc8490_phy_link_state(cvmx_phy_info_t *phy_info)
933{
934 cvmx_helper_link_info_t result;
935
936 result.s.link_up = 1;
937 result.s.full_duplex = 1;
938 result.s.speed = 1000;
939 return result;
940}
941
942static cvmx_helper_link_info_t
943__get_aquantia_phy_link_state(cvmx_phy_info_t *phy_info)
944{
945 cvmx_helper_link_info_t result;
946
947 result.s.link_up = 1;
948 result.s.full_duplex = 1;
949 result.s.speed = 1000;
950 return result;
951}
952
953static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info,
954 int ipd_port)
955{
956 const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
957 const char *compat;
958 int phy;
959 int parent;
960 u64 mdio_base;
961 int node, bus;
962 int phy_addr;
963 int index = cvmx_helper_get_interface_index_num(ipd_port);
964 int xiface = cvmx_helper_get_interface_num(ipd_port);
965 int compat_len = 0;
966
967 debug("%s(0x%p, %d) ENTER\n", __func__, phy_info, ipd_port);
968
969 phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index);
970 debug("%s: xiface: 0x%x, index: %d, ipd_port: %d, phy fdt offset: %d\n",
971 __func__, xiface, index, ipd_port, phy);
972 if (phy < 0) {
973 /* If this is the first time through we need to first parse the
974 * device tree to get the node offsets.
975 */
976 debug("No config present, calling __cvmx_helper_parse_bgx_dt\n");
977 if (__cvmx_helper_parse_bgx_dt(fdt_addr)) {
978 printf("Error: could not parse BGX device tree\n");
979 return -1;
980 }
981 if (__cvmx_fdt_parse_vsc7224(fdt_addr)) {
982 debug("Error: could not parse Microsemi VSC7224 in DT\n");
983 return -1;
984 }
985 if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV) &&
986 __cvmx_helper_parse_bgx_rgmii_dt(fdt_addr)) {
987 printf("Error: could not parse BGX XCV device tree\n");
988 return -1;
989 }
990 phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index);
991 if (phy < 0) {
992 debug("%s: Could not get PHY node offset for IPD port 0x%x, xiface: 0x%x, index: %d\n",
993 __func__, ipd_port, xiface, index);
994 return -1;
995 }
996 debug("%s: phy: %d (%s)\n", __func__, phy,
997 fdt_get_name(fdt_addr, phy, NULL));
998 }
999
1000 compat = (const char *)fdt_getprop(fdt_addr, phy, "compatible",
1001 &compat_len);
1002 if (!compat) {
1003 printf("ERROR: %d:%d:no compatible prop in phy\n", xiface,
1004 index);
1005 return -1;
1006 }
1007
1008 debug(" compatible: %s\n", compat);
1009
1010 phy_info->fdt_offset = phy;
1011 phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1);
1012 if (phy_addr == -1) {
1013 printf("Error: %d:%d:could not get PHY address\n", xiface,
1014 index);
1015 return -1;
1016 }
1017 debug(" PHY address: %d, compat: %s\n", phy_addr, compat);
1018
1019 if (!memcmp("marvell", compat, strlen("marvell"))) {
1020 phy_info->phy_type = MARVELL_GENERIC_PHY;
1021 phy_info->link_function = __get_marvell_phy_link_state;
1022 } else if (!memcmp("broadcom", compat, strlen("broadcom"))) {
1023 phy_info->phy_type = BROADCOM_GENERIC_PHY;
1024 phy_info->link_function = __get_broadcom_phy_link_state;
1025 } else if (!memcmp("cortina", compat, strlen("cortina"))) {
1026 phy_info->phy_type = CORTINA_PHY;
1027 phy_info->link_function = __cvmx_get_cortina_phy_link_state;
1028 } else if (!strcmp("vitesse,vsc8490", compat)) {
1029 phy_info->phy_type = VITESSE_VSC8490_PHY;
1030 phy_info->link_function = __get_vitesse_vsc8490_phy_link_state;
1031 } else if (fdt_stringlist_contains(compat, compat_len,
1032 "ethernet-phy-ieee802.3-c22")) {
1033 phy_info->phy_type = GENERIC_8023_C22_PHY;
1034 phy_info->link_function =
1035 __cvmx_get_generic_8023_c22_phy_link_state;
1036 } else if (fdt_stringlist_contains(compat, compat_len,
1037 "ethernet-phy-ieee802.3-c45")) {
1038 phy_info->phy_type = GENERIC_8023_C22_PHY;
1039 phy_info->link_function = __get_generic_8023_c45_phy_link_state;
1040 }
1041
1042 phy_info->ipd_port = ipd_port;
1043 phy_info->phy_sub_addr = 0;
1044 phy_info->direct_connect = 1;
1045
1046 parent = fdt_parent_offset(fdt_addr, phy);
1047 if (!fdt_node_check_compatible(fdt_addr, parent,
1048 "ethernet-phy-nexus")) {
1049 debug(" nexus PHY found\n");
1050 if (phy_info->phy_type == CORTINA_PHY) {
1051 /* The Cortina CS422X uses the same PHY device for
1052 * multiple ports for XFI. In this case we use a
1053 * nexus and each PHY address is the slice or
1054 * sub-address and the actual PHY address is the
1055 * nexus address.
1056 */
1057 phy_info->phy_sub_addr = phy_addr;
1058 phy_addr =
1059 cvmx_fdt_get_int(fdt_addr, parent, "reg", -1);
1060 debug(" Cortina PHY real address: 0x%x\n", phy_addr);
1061 }
1062 parent = fdt_parent_offset(fdt_addr, parent);
1063 }
1064
1065 debug(" Parent: %s\n", fdt_get_name(fdt_addr, parent, NULL));
1066 if (!fdt_node_check_compatible(fdt_addr, parent,
1067 "cavium,octeon-3860-mdio")) {
1068 debug(" Found Octeon MDIO\n");
1069 mdio_base = cvmx_fdt_get_uint64(fdt_addr, parent, "reg",
1070 FDT_ADDR_T_NONE);
1071 debug(" MDIO address: 0x%llx\n",
1072 (unsigned long long)mdio_base);
1073
1074 mdio_base = cvmx_fdt_translate_address(fdt_addr, parent,
1075 (u32 *)&mdio_base);
1076 debug(" Translated: 0x%llx\n", (unsigned long long)mdio_base);
1077 if (mdio_base == FDT_ADDR_T_NONE) {
1078 printf("Could not get MDIO base address from reg field\n");
1079 return -1;
1080 }
1081 __cvmx_mdio_addr_to_node_bus(mdio_base, &node, &bus);
1082 if (bus < 0) {
1083 printf("Invalid MDIO address 0x%llx, could not detect bus and node\n",
1084 (unsigned long long)mdio_base);
1085 return -1;
1086 }
1087 debug(" MDIO node: %d, bus: %d\n", node, bus);
1088 phy_info->mdio_unit = (node << 2) | (bus & 3);
1089 phy_info->phy_addr = phy_addr | (phy_info->mdio_unit << 8);
1090 } else {
1091 printf("%s: Error: incompatible MDIO bus %s for IPD port %d\n",
1092 __func__,
1093 (const char *)fdt_get_name(fdt_addr, parent, NULL),
1094 ipd_port);
1095 return -1;
1096 }
1097
1098 debug("%s: EXIT 0\n", __func__);
1099
1100 return 0;
1101}
1102
1103/**
1104 * Return the MII PHY address associated with the given IPD
1105 * port. The phy address is obtained from the device tree.
1106 *
1107 * @param[out] phy_info - PHY information data structure updated
1108 * @param ipd_port Octeon IPD port to get the MII address for.
1109 *
1110 * @return MII PHY address and bus number, -1 on error, -2 if PHY info missing (OK).
1111 */
1112static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port)
1113{
1114 const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
1115 int aliases, eth, phy, phy_parent, ret, i;
1116 int mdio_parent;
1117 const char *phy_compatible_str;
1118 const char *host_mode_str = NULL;
1119 int interface;
1120 int phy_addr_offset = 0;
1121
1122 debug("%s(%p, %d)\n", __func__, phy_info, ipd_port);
1123
1124 if (octeon_has_feature(OCTEON_FEATURE_BGX))
1125 return __cvmx_helper_78xx_parse_phy(phy_info, ipd_port);
1126
1127 phy_info->phy_addr = -1;
1128 phy_info->phy_sub_addr = 0;
1129 phy_info->ipd_port = ipd_port;
1130 phy_info->direct_connect = -1;
1131 phy_info->phy_type = (cvmx_phy_type_t)-1;
1132 for (i = 0; i < CVMX_PHY_MUX_MAX_GPIO; i++)
1133 phy_info->gpio[i] = -1;
1134 phy_info->mdio_unit = -1;
1135 phy_info->gpio_value = -1;
1136 phy_info->gpio_parent_mux_twsi = -1;
1137 phy_info->gpio_parent_mux_select = -1;
1138 phy_info->link_function = NULL;
1139 phy_info->fdt_offset = -1;
1140 if (!fdt_addr) {
1141 debug("No device tree found.\n");
1142 return -1;
1143 }
1144
1145 aliases = fdt_path_offset(fdt_addr, "/aliases");
1146 if (aliases < 0) {
1147 debug("Error: No /aliases node in device tree.\n");
1148 return -1;
1149 }
1150 if (ipd_port < 0) {
1151 int interface_index =
1152 ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
1153 eth = __mix_eth_node(fdt_addr, aliases, interface_index);
1154 } else {
1155 eth = __pip_eth_node(fdt_addr, aliases, ipd_port);
1156 }
1157 if (eth < 0) {
1158 debug("ERROR : cannot find interface for ipd_port=%d\n",
1159 ipd_port);
1160 return -1;
1161 }
1162
1163 interface = cvmx_helper_get_interface_num(ipd_port);
1164 /* Get handle to phy */
1165 phy = cvmx_fdt_lookup_phandle(fdt_addr, eth, "phy-handle");
1166 if (phy < 0) {
1167 cvmx_helper_interface_mode_t if_mode;
1168
1169 /* Note that it's OK for RXAUI and ILK to not have a PHY
1170 * connected (i.e. EBB boards in loopback).
1171 */
1172 debug("Cannot get phy-handle for ipd_port: %d\n", ipd_port);
1173 if_mode = cvmx_helper_interface_get_mode(interface);
1174 if (if_mode != CVMX_HELPER_INTERFACE_MODE_RXAUI &&
1175 if_mode != CVMX_HELPER_INTERFACE_MODE_ILK) {
1176 debug("ERROR : phy handle not found in device tree ipd_port=%d\n",
1177 ipd_port);
1178 return -1;
1179 } else {
1180 return -2;
1181 }
1182 }
1183
1184 phy_compatible_str =
1185 (const char *)fdt_getprop(fdt_addr, phy, "compatible", NULL);
1186 if (!phy_compatible_str) {
1187 debug("ERROR: no compatible prop in phy\n");
1188 return -1;
1189 }
1190 debug("Checking compatible string \"%s\" for ipd port %d\n",
1191 phy_compatible_str, ipd_port);
1192 phy_info->fdt_offset = phy;
1193 if (!memcmp("marvell", phy_compatible_str, strlen("marvell"))) {
1194 debug("Marvell PHY detected for ipd_port %d\n", ipd_port);
1195 phy_info->phy_type = MARVELL_GENERIC_PHY;
1196 phy_info->link_function = __get_marvell_phy_link_state;
1197 } else if (!memcmp("broadcom", phy_compatible_str,
1198 strlen("broadcom"))) {
1199 phy_info->phy_type = BROADCOM_GENERIC_PHY;
1200 phy_info->link_function = __get_broadcom_phy_link_state;
1201 debug("Broadcom PHY detected for ipd_port %d\n", ipd_port);
1202 } else if (!memcmp("vitesse", phy_compatible_str, strlen("vitesse"))) {
1203 debug("Vitesse PHY detected for ipd_port %d\n", ipd_port);
1204 if (!fdt_node_check_compatible(fdt_addr, phy,
1205 "vitesse,vsc8490")) {
1206 phy_info->phy_type = VITESSE_VSC8490_PHY;
1207 debug("Vitesse VSC8490 detected\n");
1208 phy_info->link_function =
1209 __get_vitesse_vsc8490_phy_link_state;
1210 } else if (!fdt_node_check_compatible(
1211 fdt_addr, phy,
1212 "ethernet-phy-ieee802.3-c22")) {
1213 phy_info->phy_type = GENERIC_8023_C22_PHY;
1214 phy_info->link_function =
1215 __cvmx_get_generic_8023_c22_phy_link_state;
1216 debug("Vitesse 802.3 c22 detected\n");
1217 } else {
1218 phy_info->phy_type = GENERIC_8023_C45_PHY;
1219 phy_info->link_function =
1220 __get_generic_8023_c45_phy_link_state;
1221 debug("Vitesse 802.3 c45 detected\n");
1222 }
1223 } else if (!memcmp("aquantia", phy_compatible_str,
1224 strlen("aquantia"))) {
1225 phy_info->phy_type = AQUANTIA_PHY;
1226 phy_info->link_function = __get_aquantia_phy_link_state;
1227 debug("Aquantia c45 PHY detected\n");
1228 } else if (!memcmp("cortina", phy_compatible_str, strlen("cortina"))) {
1229 phy_info->phy_type = CORTINA_PHY;
1230 phy_info->link_function = __cvmx_get_cortina_phy_link_state;
1231 host_mode_str = (const char *)fdt_getprop(
1232 fdt_addr, phy, "cortina,host-mode", NULL);
1233 debug("Cortina PHY detected for ipd_port %d\n", ipd_port);
1234 } else if (!memcmp("ti", phy_compatible_str, strlen("ti"))) {
1235 phy_info->phy_type = GENERIC_8023_C45_PHY;
1236 phy_info->link_function = __get_generic_8023_c45_phy_link_state;
1237 debug("TI PHY detected for ipd_port %d\n", ipd_port);
1238 } else if (!fdt_node_check_compatible(fdt_addr, phy,
1239 "atheros,ar8334") ||
1240 !fdt_node_check_compatible(fdt_addr, phy,
1241 "qualcomm,qca8334") ||
1242 !fdt_node_check_compatible(fdt_addr, phy,
1243 "atheros,ar8337") ||
1244 !fdt_node_check_compatible(fdt_addr, phy,
1245 "qualcomm,qca8337")) {
1246 phy_info->phy_type = QUALCOMM_S17;
1247 phy_info->link_function =
1248 __cvmx_get_qualcomm_s17_phy_link_state;
1249 debug("Qualcomm QCA833X switch detected\n");
1250 } else if (!fdt_node_check_compatible(fdt_addr, phy,
1251 "ethernet-phy-ieee802.3-c22")) {
1252 phy_info->phy_type = GENERIC_8023_C22_PHY;
1253 phy_info->link_function =
1254 __cvmx_get_generic_8023_c22_phy_link_state;
1255 debug("Generic 802.3 c22 PHY detected\n");
1256 } else if (!fdt_node_check_compatible(fdt_addr, phy,
1257 "ethernet-phy-ieee802.3-c45")) {
1258 phy_info->phy_type = GENERIC_8023_C45_PHY;
1259 phy_info->link_function = __get_generic_8023_c45_phy_link_state;
1260 debug("Generic 802.3 c45 PHY detected\n");
1261 } else {
1262 debug("Unknown PHY compatibility\n");
1263 phy_info->phy_type = (cvmx_phy_type_t)-1;
1264 phy_info->link_function = NULL;
1265 }
1266
1267 phy_info->host_mode = CVMX_PHY_HOST_MODE_UNKNOWN;
1268 if (host_mode_str) {
1269 if (strcmp(host_mode_str, "rxaui") == 0)
1270 phy_info->host_mode = CVMX_PHY_HOST_MODE_RXAUI;
1271 else if (strcmp(host_mode_str, "xaui") == 0)
1272 phy_info->host_mode = CVMX_PHY_HOST_MODE_XAUI;
1273 else if (strcmp(host_mode_str, "sgmii") == 0)
1274 phy_info->host_mode = CVMX_PHY_HOST_MODE_SGMII;
1275 else if (strcmp(host_mode_str, "qsgmii") == 0)
1276 phy_info->host_mode = CVMX_PHY_HOST_MODE_QSGMII;
1277 else
1278 debug("Unknown PHY host mode\n");
1279 }
1280
1281 /* Check if PHY parent is the octeon MDIO bus. Some boards are connected
1282 * though a MUX and for them direct_connect_to_phy will be 0
1283 */
1284 phy_parent = fdt_parent_offset(fdt_addr, phy);
1285 if (phy_parent < 0) {
1286 debug("ERROR : cannot find phy parent for ipd_port=%d ret=%d\n",
1287 ipd_port, phy_parent);
1288 return -1;
1289 }
1290 /* For multi-phy devices and devices on a MUX, go to the parent */
1291 ret = fdt_node_check_compatible(fdt_addr, phy_parent,
1292 "ethernet-phy-nexus");
1293 if (ret == 0) {
1294 /* It's a nexus so check the grandparent. */
1295 phy_addr_offset =
1296 cvmx_fdt_get_int(fdt_addr, phy_parent, "reg", 0);
1297 phy_parent = fdt_parent_offset(fdt_addr, phy_parent);
1298 }
1299
1300 /* Check for a muxed MDIO interface */
1301 mdio_parent = fdt_parent_offset(fdt_addr, phy_parent);
1302 ret = fdt_node_check_compatible(fdt_addr, mdio_parent,
1303 "cavium,mdio-mux");
1304 if (ret == 0) {
1305 ret = __get_muxed_mdio_info_from_dt(phy_info, phy_parent,
1306 mdio_parent);
1307 if (ret) {
1308 printf("Error reading mdio mux information for ipd port %d\n",
1309 ipd_port);
1310 return -1;
1311 }
1312 }
1313 ret = fdt_node_check_compatible(fdt_addr, phy_parent,
1314 "cavium,octeon-3860-mdio");
1315 if (ret == 0) {
1316 u32 *mdio_reg_base =
1317 (u32 *)fdt_getprop(fdt_addr, phy_parent, "reg", 0);
1318 phy_info->direct_connect = 1;
1319 if (mdio_reg_base == 0) {
1320 debug("ERROR : unable to get reg property in phy mdio\n");
1321 return -1;
1322 }
1323 phy_info->mdio_unit =
1324 __mdiobus_addr_to_unit(fdt32_to_cpu(mdio_reg_base[1]));
1325 debug("phy parent=%s reg_base=%08x mdio_unit=%d\n",
1326 fdt_get_name(fdt_addr, phy_parent, NULL),
1327 (int)mdio_reg_base[1], phy_info->mdio_unit);
1328 } else {
1329 phy_info->direct_connect = 0;
1330 /* The PHY is not directly connected to the Octeon MDIO bus.
1331 * SE doesn't have abstractions for MDIO MUX or MDIO MUX
1332 * drivers and hence for the non direct cases code will be
1333 * needed which is board specific.
1334 * For now the MDIO Unit is defaulted to 1.
1335 */
1336 debug("%s PHY at address: %d is not directly connected\n",
1337 __func__, phy_info->phy_addr);
1338 }
1339
1340 phy_info->phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1);
1341 if (phy_info->phy_addr < 0) {
1342 debug("ERROR: Could not read phy address from reg in DT\n");
1343 return -1;
1344 }
1345 phy_info->phy_addr += phy_addr_offset;
1346 phy_info->phy_addr |= phy_info->mdio_unit << 8;
1347 debug("%s(%p, %d) => 0x%x\n", __func__, phy_info, ipd_port,
1348 phy_info->phy_addr);
1349 return phy_info->phy_addr;
1350}
1351
1352/**
1353 * @INTERNAL
1354 * Parse the device tree and set whether a port is valid or not.
1355 *
1356 * @param fdt_addr Pointer to device tree
1357 *
1358 * @return 0 for success, -1 on error.
1359 */
1360int __cvmx_helper_parse_bgx_dt(const void *fdt_addr)
1361{
1362 int port_index;
1363 struct cvmx_xiface xi;
1364 int fdt_port_node = -1;
1365 int fdt_interface_node;
1366 int fdt_phy_node;
1367 u64 reg_addr;
1368 int xiface;
1369 struct cvmx_phy_info *phy_info;
1370 static bool parsed;
1371 int err;
1372 int ipd_port;
1373
1374 if (parsed) {
1375 debug("%s: Already parsed\n", __func__);
1376 return 0;
1377 }
1378 while ((fdt_port_node = fdt_node_offset_by_compatible(
1379 fdt_addr, fdt_port_node,
1380 "cavium,octeon-7890-bgx-port")) >= 0) {
1381 /* Get the port number */
1382 port_index =
1383 cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1);
1384 if (port_index < 0) {
1385 debug("Error: missing reg field for bgx port in device tree\n");
1386 return -1;
1387 }
1388 debug("%s: Parsing BGX port %d\n", __func__, port_index);
1389 /* Get the interface number */
1390 fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node);
1391 if (fdt_interface_node < 0) {
1392 debug("Error: device tree corrupt!\n");
1393 return -1;
1394 }
1395 if (fdt_node_check_compatible(fdt_addr, fdt_interface_node,
1396 "cavium,octeon-7890-bgx")) {
1397 debug("Error: incompatible Ethernet MAC Nexus in device tree!\n");
1398 return -1;
1399 }
1400 reg_addr =
1401 cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg");
1402 debug("%s: BGX interface address: 0x%llx\n", __func__,
1403 (unsigned long long)reg_addr);
1404 if (reg_addr == FDT_ADDR_T_NONE) {
1405 debug("Device tree BGX node has invalid address 0x%llx\n",
1406 (unsigned long long)reg_addr);
1407 return -1;
1408 }
1409 reg_addr = cvmx_fdt_translate_address(fdt_addr,
1410 fdt_interface_node,
1411 (u32 *)&reg_addr);
1412 xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr);
1413 if (xi.node < 0) {
1414 debug("Device tree BGX node has invalid address 0x%llx\n",
1415 (unsigned long long)reg_addr);
1416 return -1;
1417 }
1418 debug("%s: Found BGX node %d, interface %d\n", __func__,
1419 xi.node, xi.interface);
1420 xiface = cvmx_helper_node_interface_to_xiface(xi.node,
1421 xi.interface);
1422 cvmx_helper_set_port_fdt_node_offset(xiface, port_index,
1423 fdt_port_node);
1424 cvmx_helper_set_port_valid(xiface, port_index, true);
1425
1426 cvmx_helper_set_port_fdt_node_offset(xiface, port_index,
1427 fdt_port_node);
1428 if (fdt_getprop(fdt_addr, fdt_port_node,
1429 "cavium,sgmii-mac-phy-mode", NULL))
1430 cvmx_helper_set_mac_phy_mode(xiface, port_index, true);
1431 else
1432 cvmx_helper_set_mac_phy_mode(xiface, port_index, false);
1433
1434 if (fdt_getprop(fdt_addr, fdt_port_node, "cavium,force-link-up",
1435 NULL))
1436 cvmx_helper_set_port_force_link_up(xiface, port_index,
1437 true);
1438 else
1439 cvmx_helper_set_port_force_link_up(xiface, port_index,
1440 false);
1441
1442 if (fdt_getprop(fdt_addr, fdt_port_node,
1443 "cavium,sgmii-mac-1000x-mode", NULL))
1444 cvmx_helper_set_1000x_mode(xiface, port_index, true);
1445 else
1446 cvmx_helper_set_1000x_mode(xiface, port_index, false);
1447
1448 if (fdt_getprop(fdt_addr, fdt_port_node,
1449 "cavium,disable-autonegotiation", NULL))
1450 cvmx_helper_set_port_autonegotiation(xiface, port_index,
1451 false);
1452 else
1453 cvmx_helper_set_port_autonegotiation(xiface, port_index,
1454 true);
1455
1456 fdt_phy_node = cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node,
1457 "phy-handle");
1458 if (fdt_phy_node >= 0) {
1459 cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
1460 fdt_phy_node);
1461 debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n",
1462 __func__, xiface, port_index, fdt_phy_node);
1463 debug("%s: PHY node name: %s\n", __func__,
1464 fdt_get_name(fdt_addr, fdt_phy_node, NULL));
1465 cvmx_helper_set_port_phy_present(xiface, port_index,
1466 true);
1467 ipd_port = cvmx_helper_get_ipd_port(xiface, port_index);
1468 if (ipd_port >= 0) {
1469 debug("%s: Allocating phy info for 0x%x:%d\n",
1470 __func__, xiface, port_index);
1471 phy_info =
1472 (cvmx_phy_info_t *)cvmx_bootmem_alloc(
1473 sizeof(*phy_info), 0);
1474 if (!phy_info) {
1475 debug("%s: Out of memory\n", __func__);
1476 return -1;
1477 }
1478 memset(phy_info, 0, sizeof(*phy_info));
1479 phy_info->phy_addr = -1;
1480 err = __get_phy_info_from_dt(phy_info,
1481 ipd_port);
1482 if (err) {
1483 debug("%s: Error parsing phy info for ipd port %d\n",
1484 __func__, ipd_port);
1485 return -1;
1486 }
1487 cvmx_helper_set_port_phy_info(
1488 xiface, port_index, phy_info);
1489 debug("%s: Saved phy info\n", __func__);
1490 }
1491 } else {
1492 cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
1493 -1);
1494 debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n",
1495 __func__, xiface, port_index, fdt_phy_node);
1496 cvmx_helper_set_port_phy_present(xiface, port_index,
1497 false);
1498 }
1499 }
1500 if (!sfp_parsed)
1501 if (cvmx_sfp_parse_device_tree(fdt_addr))
1502 debug("%s: Error parsing SFP device tree\n", __func__);
1503 parsed = true;
1504 return 0;
1505}
1506
1507int __cvmx_helper_parse_bgx_rgmii_dt(const void *fdt_addr)
1508{
1509 u64 reg_addr;
1510 struct cvmx_xiface xi;
1511 int fdt_port_node = -1;
1512 int fdt_interface_node;
1513 int fdt_phy_node;
1514 int port_index;
1515 int xiface;
1516
1517 /* There's only one xcv (RGMII) interface, so just search for the one
1518 * that's part of a BGX entry.
1519 */
1520 while ((fdt_port_node = fdt_node_offset_by_compatible(
1521 fdt_addr, fdt_port_node, "cavium,octeon-7360-xcv")) >=
1522 0) {
1523 fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node);
1524 if (fdt_interface_node < 0) {
1525 printf("Error: device tree corrupt!\n");
1526 return -1;
1527 }
1528 debug("%s: XCV parent node compatible: %s\n", __func__,
1529 (char *)fdt_getprop(fdt_addr, fdt_interface_node,
1530 "compatible", NULL));
1531 if (!fdt_node_check_compatible(fdt_addr, fdt_interface_node,
1532 "cavium,octeon-7890-bgx"))
1533 break;
1534 }
1535 if (fdt_port_node == -FDT_ERR_NOTFOUND) {
1536 debug("No XCV/RGMII interface found in device tree\n");
1537 return 0;
1538 } else if (fdt_port_node < 0) {
1539 debug("%s: Error %d parsing device tree\n", __func__,
1540 fdt_port_node);
1541 return -1;
1542 }
1543 port_index = cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1);
1544 if (port_index != 0) {
1545 printf("%s: Error: port index (reg) must be 0, not %d.\n",
1546 __func__, port_index);
1547 return -1;
1548 }
1549 reg_addr = cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg");
1550 if (reg_addr == FDT_ADDR_T_NONE) {
1551 printf("%s: Error: could not get BGX interface address\n",
1552 __func__);
1553 return -1;
1554 }
1555 /* We don't have to bother translating since only 78xx supports OCX and
1556 * doesn't support RGMII.
1557 */
1558 xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr);
1559 debug("%s: xi.node: %d, xi.interface: 0x%x, addr: 0x%llx\n", __func__,
1560 xi.node, xi.interface, (unsigned long long)reg_addr);
1561 if (xi.node < 0) {
1562 printf("%s: Device tree BGX node has invalid address 0x%llx\n",
1563 __func__, (unsigned long long)reg_addr);
1564 return -1;
1565 }
1566 debug("%s: Found XCV (RGMII) interface on interface %d\n", __func__,
1567 xi.interface);
1568 debug(" phy handle: 0x%x\n",
1569 cvmx_fdt_get_int(fdt_addr, fdt_port_node, "phy-handle", -1));
1570 fdt_phy_node =
1571 cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node, "phy-handle");
1572 debug("%s: phy-handle node: 0x%x\n", __func__, fdt_phy_node);
1573 xiface = cvmx_helper_node_interface_to_xiface(xi.node, xi.interface);
1574
1575 cvmx_helper_set_port_fdt_node_offset(xiface, port_index, fdt_port_node);
1576 if (fdt_phy_node >= 0) {
1577 debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n",
1578 __func__, xiface, port_index, fdt_phy_node);
1579 debug("%s: PHY node name: %s\n", __func__,
1580 fdt_get_name(fdt_addr, fdt_phy_node, NULL));
1581 cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
1582 fdt_phy_node);
1583 cvmx_helper_set_port_phy_present(xiface, port_index, true);
1584 } else {
1585 cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, -1);
1586 debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n",
1587 __func__, xiface, port_index, fdt_phy_node);
1588 cvmx_helper_set_port_phy_present(xiface, port_index, false);
1589 }
1590
1591 return 0;
1592}
1593
1594/**
1595 * Returns if a port is present on an interface
1596 *
1597 * @param fdt_addr - address fo flat device tree
1598 * @param ipd_port - IPD port number
1599 *
1600 * @return 1 if port is present, 0 if not present, -1 if error
1601 */
1602int __cvmx_helper_board_get_port_from_dt(void *fdt_addr, int ipd_port)
1603{
1604 int port_index;
1605 int aliases;
1606 const char *pip_path;
1607 char name_buffer[24];
1608 int pip, iface, eth;
1609 cvmx_helper_interface_mode_t mode;
1610 int xiface = cvmx_helper_get_interface_num(ipd_port);
1611 struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
1612 u32 val;
1613 int phy_node_offset;
1614 int parse_bgx_dt_err;
1615 int parse_vsc7224_err;
1616
1617 debug("%s(%p, %d)\n", __func__, fdt_addr, ipd_port);
1618 if (octeon_has_feature(OCTEON_FEATURE_BGX)) {
1619 static int fdt_ports_initialized;
1620
1621 port_index = cvmx_helper_get_interface_index_num(ipd_port);
1622
1623 if (!fdt_ports_initialized) {
1624 if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV)) {
1625 if (!__cvmx_helper_parse_bgx_rgmii_dt(fdt_addr))
1626 fdt_ports_initialized = 1;
1627 parse_bgx_dt_err =
1628 __cvmx_helper_parse_bgx_dt(fdt_addr);
1629 parse_vsc7224_err =
1630 __cvmx_fdt_parse_vsc7224(fdt_addr);
1631 if (!parse_bgx_dt_err && !parse_vsc7224_err)
1632 fdt_ports_initialized = 1;
1633 } else {
1634 debug("%s: Error parsing FDT\n", __func__);
1635 return -1;
1636 }
1637 }
1638
1639 return cvmx_helper_is_port_valid(xiface, port_index);
1640 }
1641
1642 mode = cvmx_helper_interface_get_mode(xiface);
1643
1644 switch (mode) {
1645 /* Device tree has information about the following mode types. */
1646 case CVMX_HELPER_INTERFACE_MODE_RGMII:
1647 case CVMX_HELPER_INTERFACE_MODE_GMII:
1648 case CVMX_HELPER_INTERFACE_MODE_SPI:
1649 case CVMX_HELPER_INTERFACE_MODE_XAUI:
1650 case CVMX_HELPER_INTERFACE_MODE_SGMII:
1651 case CVMX_HELPER_INTERFACE_MODE_QSGMII:
1652 case CVMX_HELPER_INTERFACE_MODE_RXAUI:
1653 case CVMX_HELPER_INTERFACE_MODE_AGL:
1654 case CVMX_HELPER_INTERFACE_MODE_XLAUI:
1655 case CVMX_HELPER_INTERFACE_MODE_XFI:
1656 aliases = 1;
1657 break;
1658 default:
1659 aliases = 0;
1660 break;
1661 }
1662
1663 /* The device tree information is present on interfaces that have phy */
1664 if (!aliases)
1665 return 1;
1666
1667 port_index = cvmx_helper_get_interface_index_num(ipd_port);
1668
1669 aliases = fdt_path_offset(fdt_addr, "/aliases");
1670 if (aliases < 0) {
1671 debug("%s: ERROR: /aliases not found in device tree fdt_addr=%p\n",
1672 __func__, fdt_addr);
1673 return -1;
1674 }
1675
1676 pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL);
1677 if (!pip_path) {
1678 debug("%s: ERROR: interface %x pip path not found in device tree\n",
1679 __func__, xiface);
1680 return -1;
1681 }
1682 pip = fdt_path_offset(fdt_addr, pip_path);
1683 if (pip < 0) {
1684 debug("%s: ERROR: interface %x pip not found in device tree\n",
1685 __func__, xiface);
1686 return -1;
1687 }
1688 snprintf(name_buffer, sizeof(name_buffer), "interface@%d",
1689 xi.interface);
1690 iface = fdt_subnode_offset(fdt_addr, pip, name_buffer);
1691 if (iface < 0)
1692 return 0;
1693 snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", port_index);
1694 eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
1695 debug("%s: eth subnode offset %d from %s\n", __func__, eth,
1696 name_buffer);
1697
1698 if (eth < 0)
1699 return -1;
1700
1701 cvmx_helper_set_port_fdt_node_offset(xiface, port_index, eth);
1702
1703 phy_node_offset = cvmx_fdt_get_int(fdt_addr, eth, "phy", -1);
1704 cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
1705 phy_node_offset);
1706
1707 if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-phy-mode", NULL))
1708 cvmx_helper_set_mac_phy_mode(xiface, port_index, true);
1709 else
1710 cvmx_helper_set_mac_phy_mode(xiface, port_index, false);
1711
1712 if (fdt_getprop(fdt_addr, eth, "cavium,force-link-up", NULL))
1713 cvmx_helper_set_port_force_link_up(xiface, port_index, true);
1714 else
1715 cvmx_helper_set_port_force_link_up(xiface, port_index, false);
1716
1717 if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-1000x-mode", NULL))
1718 cvmx_helper_set_1000x_mode(xiface, port_index, true);
1719 else
1720 cvmx_helper_set_1000x_mode(xiface, port_index, false);
1721
1722 if (fdt_getprop(fdt_addr, eth, "cavium,disable-autonegotiation", NULL))
1723 cvmx_helper_set_port_autonegotiation(xiface, port_index, false);
1724 else
1725 cvmx_helper_set_port_autonegotiation(xiface, port_index, true);
1726
1727 if (mode == CVMX_HELPER_INTERFACE_MODE_AGL) {
1728 bool tx_bypass = false;
1729
1730 if (fdt_getprop(fdt_addr, eth, "cavium,rx-clk-delay-bypass",
1731 NULL))
1732 cvmx_helper_set_agl_rx_clock_delay_bypass(
1733 xiface, port_index, true);
1734 else
1735 cvmx_helper_set_agl_rx_clock_delay_bypass(
1736 xiface, port_index, false);
1737
1738 val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,rx-clk-skew", 0);
1739 cvmx_helper_set_agl_rx_clock_skew(xiface, port_index, val);
1740
1741 if (fdt_getprop(fdt_addr, eth, "cavium,tx-clk-delay-bypass",
1742 NULL))
1743 tx_bypass = true;
1744
1745 val = cvmx_fdt_get_int(fdt_addr, eth, "tx-clk-delay", 0);
1746 cvmx_helper_cfg_set_rgmii_tx_clk_delay(xiface, port_index,
1747 tx_bypass, val);
1748
1749 val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,refclk-sel", 0);
1750 cvmx_helper_set_agl_refclk_sel(xiface, port_index, val);
1751 }
1752
1753 return (eth >= 0);
1754}
1755
1756/**
1757 * Given the address of the MDIO registers, output the CPU node and MDIO bus
1758 *
1759 * @param addr 64-bit address of MDIO registers (from device tree)
1760 * @param[out] node CPU node number (78xx)
1761 * @param[out] bus MDIO bus number
1762 */
1763void __cvmx_mdio_addr_to_node_bus(u64 addr, int *node, int *bus)
1764{
1765 if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
1766 if (node)
1767 *node = cvmx_csr_addr_to_node(addr);
1768 addr = cvmx_csr_addr_strip_node(addr);
1769 } else {
1770 if (node)
1771 *node = 0;
1772 }
1773 if (OCTEON_IS_MODEL(OCTEON_CN68XX) || OCTEON_IS_MODEL(OCTEON_CN78XX)) {
1774 switch (addr) {
1775 case 0x0001180000003800:
1776 *bus = 0;
1777 break;
1778 case 0x0001180000003880:
1779 *bus = 1;
1780 break;
1781 case 0x0001180000003900:
1782 *bus = 2;
1783 break;
1784 case 0x0001180000003980:
1785 *bus = 3;
1786 break;
1787 default:
1788 *bus = -1;
1789 printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
1790 (unsigned long long)addr);
1791 break;
1792 }
1793 } else if (OCTEON_IS_MODEL(OCTEON_CN73XX) ||
1794 OCTEON_IS_MODEL(OCTEON_CNF75XX)) {
1795 switch (addr) {
1796 case 0x0001180000003800:
1797 *bus = 0;
1798 break;
1799 case 0x0001180000003880:
1800 *bus = 1;
1801 break;
1802 default:
1803 *bus = -1;
1804 printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
1805 (unsigned long long)addr);
1806 break;
1807 }
1808 } else {
1809 switch (addr) {
1810 case 0x0001180000001800:
1811 *bus = 0;
1812 break;
1813 case 0x0001180000001900:
1814 *bus = 1;
1815 break;
1816 default:
1817 *bus = -1;
1818 printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
1819 (unsigned long long)addr);
1820 break;
1821 }
1822 }
1823}