| #include <dm.h> |
| #include <miiphy.h> |
| #include <asm-generic/gpio.h> |
| #include <linux/bitops.h> |
| #include <linux/delay.h> |
| |
| #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; |
| |
| phydev->interface = PHY_INTERFACE_MODE_MII; |
| 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; |
| |
| phydev = phy_find_by_mask(bus, BIT(m * 8 + phy_idx)); |
| 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); |