#include #include #include #include #include #include #include "ihs_phys.h" #include "dt_helpers.h" enum { PORTTYPE_MAIN_CAT, PORTTYPE_TOP_CAT, PORTTYPE_16C_16F, PORTTYPE_UNKNOWN }; static struct porttype { bool phy_invert_in_pol; bool phy_invert_out_pol; } porttypes[] = { { true, false }, { false, true }, { false, false }, }; static void ihs_phy_config(struct phy_device *phydev, bool qinpn, bool qoutpn) { u16 reg; phy_config(phydev); /* enable QSGMII autonegotiation with flow control */ phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0004); reg = phy_read(phydev, MDIO_DEVAD_NONE, 16); reg |= (3 << 6); phy_write(phydev, MDIO_DEVAD_NONE, 16, reg); /* * invert QSGMII Q_INP/N and Q_OUTP/N if required * and perform global reset */ reg = phy_read(phydev, MDIO_DEVAD_NONE, 26); if (qinpn) reg |= (1 << 13); if (qoutpn) reg |= (1 << 12); reg |= (1 << 15); phy_write(phydev, MDIO_DEVAD_NONE, 26, reg); /* advertise 1000BASE-T full-duplex only */ phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0000); reg = phy_read(phydev, MDIO_DEVAD_NONE, 4); reg &= ~0x1e0; phy_write(phydev, MDIO_DEVAD_NONE, 4, reg); reg = phy_read(phydev, MDIO_DEVAD_NONE, 9); reg = (reg & ~0x300) | 0x200; phy_write(phydev, MDIO_DEVAD_NONE, 9, reg); /* copper power up */ reg = phy_read(phydev, MDIO_DEVAD_NONE, 16); reg &= ~0x0004; phy_write(phydev, MDIO_DEVAD_NONE, 16, reg); } uint calculate_octo_phy_mask(void) { uint k; uint octo_phy_mask = 0; struct gpio_desc gpio = {}; char gpio_name[64]; static const char * const dev_name[] = {"pca9698@23", "pca9698@21", "pca9698@24", "pca9698@25", "pca9698@26"}; /* mark all octo phys that should be present */ for (k = 0; k < 5; ++k) { snprintf(gpio_name, 64, "cat-gpio-%u", k); if (request_gpio_by_name(&gpio, dev_name[k], 0x20, gpio_name)) continue; /* check CAT flag */ if (dm_gpio_get_value(&gpio)) octo_phy_mask |= (1 << (k * 2)); else /* If CAT == 0, there's no second octo phy -> skip */ continue; snprintf(gpio_name, 64, "second-octo-gpio-%u", k); if (request_gpio_by_name(&gpio, dev_name[k], 0x27, gpio_name)) { /* default: second octo phy is present */ octo_phy_mask |= (1 << (k * 2 + 1)); continue; } if (dm_gpio_get_value(&gpio) == 0) octo_phy_mask |= (1 << (k * 2 + 1)); } return octo_phy_mask; } int register_miiphy_bus(uint k, struct mii_dev **bus) { int retval; struct mii_dev *mdiodev = mdio_alloc(); char *name = bb_miiphy_buses[k].name; if (!mdiodev) return -ENOMEM; strlcpy(mdiodev->name, name, MDIO_NAME_LEN); mdiodev->read = bb_miiphy_read; mdiodev->write = bb_miiphy_write; retval = mdio_register(mdiodev); if (retval < 0) return retval; *bus = miiphy_get_dev_by_name(name); return 0; } struct porttype *get_porttype(uint octo_phy_mask, uint k) { uint octo_index = k * 4; if (!k) { if (octo_phy_mask & 0x01) return &porttypes[PORTTYPE_MAIN_CAT]; else if (!(octo_phy_mask & 0x03)) return &porttypes[PORTTYPE_16C_16F]; } else { if (octo_phy_mask & (1 << octo_index)) return &porttypes[PORTTYPE_TOP_CAT]; } return NULL; } int init_single_phy(struct porttype *porttype, struct mii_dev *bus, uint bus_idx, uint m, uint phy_idx) { struct phy_device *phydev = phy_find_by_mask( bus, 1 << (m * 8 + phy_idx), PHY_INTERFACE_MODE_MII); printf(" %u", bus_idx * 32 + m * 8 + phy_idx); if (!phydev) puts("!"); else ihs_phy_config(phydev, porttype->phy_invert_in_pol, porttype->phy_invert_out_pol); return 0; } int init_octo_phys(uint octo_phy_mask) { uint bus_idx; /* there are up to four octo-phys on each mdio bus */ for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; ++bus_idx) { uint m; uint octo_index = bus_idx * 4; struct mii_dev *bus = NULL; struct porttype *porttype = NULL; int ret; porttype = get_porttype(octo_phy_mask, bus_idx); if (!porttype) continue; for (m = 0; m < 4; ++m) { uint phy_idx; /** * Register a bus device if there is at least one phy * on the current bus */ if (!m && octo_phy_mask & (0xf << octo_index)) { ret = register_miiphy_bus(bus_idx, &bus); if (ret) return ret; } if (!(octo_phy_mask & BIT(octo_index + m))) continue; for (phy_idx = 0; phy_idx < 8; ++phy_idx) init_single_phy(porttype, bus, bus_idx, m, phy_idx); } } return 0; } /* * MII GPIO bitbang implementation * MDC MDIO bus * 13 14 PHY1-4 * 25 45 PHY5-8 * 46 24 PHY9-10 */ struct gpio_mii { int index; struct gpio_desc mdc_gpio; struct gpio_desc mdio_gpio; int mdc_num; int mdio_num; int mdio_value; } gpio_mii_set[] = { { 0, {}, {}, 13, 14, 1 }, { 1, {}, {}, 25, 45, 1 }, { 2, {}, {}, 46, 24, 1 }, }; static int mii_mdio_init(struct bb_miiphy_bus *bus) { struct gpio_mii *gpio_mii = bus->priv; char name[32] = {}; struct udevice *gpio_dev1 = NULL; struct udevice *gpio_dev2 = NULL; if (uclass_get_device_by_name(UCLASS_GPIO, "gpio@18100", &gpio_dev1) || uclass_get_device_by_name(UCLASS_GPIO, "gpio@18140", &gpio_dev2)) { printf("Could not get GPIO device.\n"); return 1; } if (gpio_mii->mdc_num > 31) { gpio_mii->mdc_gpio.dev = gpio_dev2; gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num - 32; } else { gpio_mii->mdc_gpio.dev = gpio_dev1; gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num; } gpio_mii->mdc_gpio.flags = 0; snprintf(name, 32, "bb_miiphy_bus-%d-mdc", gpio_mii->index); dm_gpio_request(&gpio_mii->mdc_gpio, name); if (gpio_mii->mdio_num > 31) { gpio_mii->mdio_gpio.dev = gpio_dev2; gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num - 32; } else { gpio_mii->mdio_gpio.dev = gpio_dev1; gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num; } gpio_mii->mdio_gpio.flags = 0; snprintf(name, 32, "bb_miiphy_bus-%d-mdio", gpio_mii->index); dm_gpio_request(&gpio_mii->mdio_gpio, name); dm_gpio_set_dir_flags(&gpio_mii->mdc_gpio, GPIOD_IS_OUT); dm_gpio_set_value(&gpio_mii->mdc_gpio, 1); return 0; } static int mii_mdio_active(struct bb_miiphy_bus *bus) { struct gpio_mii *gpio_mii = bus->priv; dm_gpio_set_value(&gpio_mii->mdc_gpio, gpio_mii->mdio_value); return 0; } static int mii_mdio_tristate(struct bb_miiphy_bus *bus) { struct gpio_mii *gpio_mii = bus->priv; dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN); return 0; } static int mii_set_mdio(struct bb_miiphy_bus *bus, int v) { struct gpio_mii *gpio_mii = bus->priv; dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_OUT); dm_gpio_set_value(&gpio_mii->mdio_gpio, v); gpio_mii->mdio_value = v; return 0; } static int mii_get_mdio(struct bb_miiphy_bus *bus, int *v) { struct gpio_mii *gpio_mii = bus->priv; dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN); *v = (dm_gpio_get_value(&gpio_mii->mdio_gpio)); return 0; } static int mii_set_mdc(struct bb_miiphy_bus *bus, int v) { struct gpio_mii *gpio_mii = bus->priv; dm_gpio_set_value(&gpio_mii->mdc_gpio, v); return 0; } static int mii_delay(struct bb_miiphy_bus *bus) { udelay(1); return 0; } struct bb_miiphy_bus bb_miiphy_buses[] = { { .name = "ihs0", .init = mii_mdio_init, .mdio_active = mii_mdio_active, .mdio_tristate = mii_mdio_tristate, .set_mdio = mii_set_mdio, .get_mdio = mii_get_mdio, .set_mdc = mii_set_mdc, .delay = mii_delay, .priv = &gpio_mii_set[0], }, { .name = "ihs1", .init = mii_mdio_init, .mdio_active = mii_mdio_active, .mdio_tristate = mii_mdio_tristate, .set_mdio = mii_set_mdio, .get_mdio = mii_get_mdio, .set_mdc = mii_set_mdc, .delay = mii_delay, .priv = &gpio_mii_set[1], }, { .name = "ihs2", .init = mii_mdio_init, .mdio_active = mii_mdio_active, .mdio_tristate = mii_mdio_tristate, .set_mdio = mii_set_mdio, .get_mdio = mii_get_mdio, .set_mdc = mii_set_mdc, .delay = mii_delay, .priv = &gpio_mii_set[2], }, }; int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);