Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 1 | #include <dm.h> |
| 2 | #include <miiphy.h> |
| 3 | #include <asm-generic/gpio.h> |
Simon Glass | 4dcacfc | 2020-05-10 11:40:13 -0600 | [diff] [blame] | 4 | #include <linux/bitops.h> |
Simon Glass | dbd7954 | 2020-05-10 11:40:11 -0600 | [diff] [blame] | 5 | #include <linux/delay.h> |
Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 6 | |
| 7 | #include "ihs_phys.h" |
| 8 | #include "dt_helpers.h" |
| 9 | |
| 10 | enum { |
| 11 | PORTTYPE_MAIN_CAT, |
| 12 | PORTTYPE_TOP_CAT, |
| 13 | PORTTYPE_16C_16F, |
| 14 | PORTTYPE_UNKNOWN |
| 15 | }; |
| 16 | |
| 17 | static struct porttype { |
| 18 | bool phy_invert_in_pol; |
| 19 | bool phy_invert_out_pol; |
| 20 | } porttypes[] = { |
| 21 | { true, false }, |
| 22 | { false, true }, |
| 23 | { false, false }, |
| 24 | }; |
| 25 | |
| 26 | static void ihs_phy_config(struct phy_device *phydev, bool qinpn, bool qoutpn) |
| 27 | { |
| 28 | u16 reg; |
| 29 | |
Marek Behún | 3927efb | 2022-04-07 00:33:08 +0200 | [diff] [blame] | 30 | phydev->interface = PHY_INTERFACE_MODE_MII; |
Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 31 | phy_config(phydev); |
| 32 | |
| 33 | /* enable QSGMII autonegotiation with flow control */ |
| 34 | phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0004); |
| 35 | reg = phy_read(phydev, MDIO_DEVAD_NONE, 16); |
| 36 | reg |= (3 << 6); |
| 37 | phy_write(phydev, MDIO_DEVAD_NONE, 16, reg); |
| 38 | |
| 39 | /* |
| 40 | * invert QSGMII Q_INP/N and Q_OUTP/N if required |
| 41 | * and perform global reset |
| 42 | */ |
| 43 | reg = phy_read(phydev, MDIO_DEVAD_NONE, 26); |
| 44 | if (qinpn) |
| 45 | reg |= (1 << 13); |
| 46 | if (qoutpn) |
| 47 | reg |= (1 << 12); |
| 48 | reg |= (1 << 15); |
| 49 | phy_write(phydev, MDIO_DEVAD_NONE, 26, reg); |
| 50 | |
| 51 | /* advertise 1000BASE-T full-duplex only */ |
| 52 | phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0000); |
| 53 | reg = phy_read(phydev, MDIO_DEVAD_NONE, 4); |
| 54 | reg &= ~0x1e0; |
| 55 | phy_write(phydev, MDIO_DEVAD_NONE, 4, reg); |
| 56 | reg = phy_read(phydev, MDIO_DEVAD_NONE, 9); |
| 57 | reg = (reg & ~0x300) | 0x200; |
| 58 | phy_write(phydev, MDIO_DEVAD_NONE, 9, reg); |
| 59 | |
| 60 | /* copper power up */ |
| 61 | reg = phy_read(phydev, MDIO_DEVAD_NONE, 16); |
| 62 | reg &= ~0x0004; |
| 63 | phy_write(phydev, MDIO_DEVAD_NONE, 16, reg); |
| 64 | } |
| 65 | |
| 66 | uint calculate_octo_phy_mask(void) |
| 67 | { |
| 68 | uint k; |
| 69 | uint octo_phy_mask = 0; |
| 70 | struct gpio_desc gpio = {}; |
| 71 | char gpio_name[64]; |
| 72 | static const char * const dev_name[] = {"pca9698@23", "pca9698@21", |
| 73 | "pca9698@24", "pca9698@25", |
| 74 | "pca9698@26"}; |
| 75 | |
| 76 | /* mark all octo phys that should be present */ |
| 77 | for (k = 0; k < 5; ++k) { |
| 78 | snprintf(gpio_name, 64, "cat-gpio-%u", k); |
| 79 | |
| 80 | if (request_gpio_by_name(&gpio, dev_name[k], 0x20, gpio_name)) |
| 81 | continue; |
| 82 | |
| 83 | /* check CAT flag */ |
| 84 | if (dm_gpio_get_value(&gpio)) |
| 85 | octo_phy_mask |= (1 << (k * 2)); |
| 86 | else |
| 87 | /* If CAT == 0, there's no second octo phy -> skip */ |
| 88 | continue; |
| 89 | |
| 90 | snprintf(gpio_name, 64, "second-octo-gpio-%u", k); |
| 91 | |
| 92 | if (request_gpio_by_name(&gpio, dev_name[k], 0x27, gpio_name)) { |
| 93 | /* default: second octo phy is present */ |
| 94 | octo_phy_mask |= (1 << (k * 2 + 1)); |
| 95 | continue; |
| 96 | } |
| 97 | |
| 98 | if (dm_gpio_get_value(&gpio) == 0) |
| 99 | octo_phy_mask |= (1 << (k * 2 + 1)); |
| 100 | } |
| 101 | |
| 102 | return octo_phy_mask; |
| 103 | } |
| 104 | |
| 105 | int register_miiphy_bus(uint k, struct mii_dev **bus) |
| 106 | { |
| 107 | int retval; |
| 108 | struct mii_dev *mdiodev = mdio_alloc(); |
| 109 | char *name = bb_miiphy_buses[k].name; |
| 110 | |
| 111 | if (!mdiodev) |
| 112 | return -ENOMEM; |
Vladimir Oltean | bf2f900 | 2021-09-27 14:21:42 +0300 | [diff] [blame] | 113 | strlcpy(mdiodev->name, name, MDIO_NAME_LEN); |
Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 114 | mdiodev->read = bb_miiphy_read; |
| 115 | mdiodev->write = bb_miiphy_write; |
| 116 | |
| 117 | retval = mdio_register(mdiodev); |
| 118 | if (retval < 0) |
| 119 | return retval; |
| 120 | *bus = miiphy_get_dev_by_name(name); |
| 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | |
| 125 | struct porttype *get_porttype(uint octo_phy_mask, uint k) |
| 126 | { |
| 127 | uint octo_index = k * 4; |
| 128 | |
| 129 | if (!k) { |
| 130 | if (octo_phy_mask & 0x01) |
| 131 | return &porttypes[PORTTYPE_MAIN_CAT]; |
| 132 | else if (!(octo_phy_mask & 0x03)) |
| 133 | return &porttypes[PORTTYPE_16C_16F]; |
| 134 | } else { |
| 135 | if (octo_phy_mask & (1 << octo_index)) |
| 136 | return &porttypes[PORTTYPE_TOP_CAT]; |
| 137 | } |
| 138 | |
| 139 | return NULL; |
| 140 | } |
| 141 | |
| 142 | int init_single_phy(struct porttype *porttype, struct mii_dev *bus, |
| 143 | uint bus_idx, uint m, uint phy_idx) |
| 144 | { |
Marek Behún | 3927efb | 2022-04-07 00:33:08 +0200 | [diff] [blame] | 145 | struct phy_device *phydev; |
Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 146 | |
Marek Behún | 3927efb | 2022-04-07 00:33:08 +0200 | [diff] [blame] | 147 | phydev = phy_find_by_mask(bus, BIT(m * 8 + phy_idx)); |
Dirk Eibach | fb60594 | 2017-02-22 16:07:23 +0100 | [diff] [blame] | 148 | printf(" %u", bus_idx * 32 + m * 8 + phy_idx); |
| 149 | |
| 150 | if (!phydev) |
| 151 | puts("!"); |
| 152 | else |
| 153 | ihs_phy_config(phydev, porttype->phy_invert_in_pol, |
| 154 | porttype->phy_invert_out_pol); |
| 155 | |
| 156 | return 0; |
| 157 | } |
| 158 | |
| 159 | int init_octo_phys(uint octo_phy_mask) |
| 160 | { |
| 161 | uint bus_idx; |
| 162 | |
| 163 | /* there are up to four octo-phys on each mdio bus */ |
| 164 | for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; ++bus_idx) { |
| 165 | uint m; |
| 166 | uint octo_index = bus_idx * 4; |
| 167 | struct mii_dev *bus = NULL; |
| 168 | struct porttype *porttype = NULL; |
| 169 | int ret; |
| 170 | |
| 171 | porttype = get_porttype(octo_phy_mask, bus_idx); |
| 172 | |
| 173 | if (!porttype) |
| 174 | continue; |
| 175 | |
| 176 | for (m = 0; m < 4; ++m) { |
| 177 | uint phy_idx; |
| 178 | |
| 179 | /** |
| 180 | * Register a bus device if there is at least one phy |
| 181 | * on the current bus |
| 182 | */ |
| 183 | if (!m && octo_phy_mask & (0xf << octo_index)) { |
| 184 | ret = register_miiphy_bus(bus_idx, &bus); |
| 185 | if (ret) |
| 186 | return ret; |
| 187 | } |
| 188 | |
| 189 | if (!(octo_phy_mask & BIT(octo_index + m))) |
| 190 | continue; |
| 191 | |
| 192 | for (phy_idx = 0; phy_idx < 8; ++phy_idx) |
| 193 | init_single_phy(porttype, bus, bus_idx, m, |
| 194 | phy_idx); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | return 0; |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | * MII GPIO bitbang implementation |
| 203 | * MDC MDIO bus |
| 204 | * 13 14 PHY1-4 |
| 205 | * 25 45 PHY5-8 |
| 206 | * 46 24 PHY9-10 |
| 207 | */ |
| 208 | |
| 209 | struct gpio_mii { |
| 210 | int index; |
| 211 | struct gpio_desc mdc_gpio; |
| 212 | struct gpio_desc mdio_gpio; |
| 213 | int mdc_num; |
| 214 | int mdio_num; |
| 215 | int mdio_value; |
| 216 | } gpio_mii_set[] = { |
| 217 | { 0, {}, {}, 13, 14, 1 }, |
| 218 | { 1, {}, {}, 25, 45, 1 }, |
| 219 | { 2, {}, {}, 46, 24, 1 }, |
| 220 | }; |
| 221 | |
| 222 | static int mii_mdio_init(struct bb_miiphy_bus *bus) |
| 223 | { |
| 224 | struct gpio_mii *gpio_mii = bus->priv; |
| 225 | char name[32] = {}; |
| 226 | struct udevice *gpio_dev1 = NULL; |
| 227 | struct udevice *gpio_dev2 = NULL; |
| 228 | |
| 229 | if (uclass_get_device_by_name(UCLASS_GPIO, "gpio@18100", &gpio_dev1) || |
| 230 | uclass_get_device_by_name(UCLASS_GPIO, "gpio@18140", &gpio_dev2)) { |
| 231 | printf("Could not get GPIO device.\n"); |
| 232 | return 1; |
| 233 | } |
| 234 | |
| 235 | if (gpio_mii->mdc_num > 31) { |
| 236 | gpio_mii->mdc_gpio.dev = gpio_dev2; |
| 237 | gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num - 32; |
| 238 | } else { |
| 239 | gpio_mii->mdc_gpio.dev = gpio_dev1; |
| 240 | gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num; |
| 241 | } |
| 242 | gpio_mii->mdc_gpio.flags = 0; |
| 243 | snprintf(name, 32, "bb_miiphy_bus-%d-mdc", gpio_mii->index); |
| 244 | dm_gpio_request(&gpio_mii->mdc_gpio, name); |
| 245 | |
| 246 | if (gpio_mii->mdio_num > 31) { |
| 247 | gpio_mii->mdio_gpio.dev = gpio_dev2; |
| 248 | gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num - 32; |
| 249 | } else { |
| 250 | gpio_mii->mdio_gpio.dev = gpio_dev1; |
| 251 | gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num; |
| 252 | } |
| 253 | gpio_mii->mdio_gpio.flags = 0; |
| 254 | snprintf(name, 32, "bb_miiphy_bus-%d-mdio", gpio_mii->index); |
| 255 | dm_gpio_request(&gpio_mii->mdio_gpio, name); |
| 256 | |
| 257 | dm_gpio_set_dir_flags(&gpio_mii->mdc_gpio, GPIOD_IS_OUT); |
| 258 | dm_gpio_set_value(&gpio_mii->mdc_gpio, 1); |
| 259 | |
| 260 | return 0; |
| 261 | } |
| 262 | |
| 263 | static int mii_mdio_active(struct bb_miiphy_bus *bus) |
| 264 | { |
| 265 | struct gpio_mii *gpio_mii = bus->priv; |
| 266 | |
| 267 | dm_gpio_set_value(&gpio_mii->mdc_gpio, gpio_mii->mdio_value); |
| 268 | |
| 269 | return 0; |
| 270 | } |
| 271 | |
| 272 | static int mii_mdio_tristate(struct bb_miiphy_bus *bus) |
| 273 | { |
| 274 | struct gpio_mii *gpio_mii = bus->priv; |
| 275 | |
| 276 | dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN); |
| 277 | |
| 278 | return 0; |
| 279 | } |
| 280 | |
| 281 | static int mii_set_mdio(struct bb_miiphy_bus *bus, int v) |
| 282 | { |
| 283 | struct gpio_mii *gpio_mii = bus->priv; |
| 284 | |
| 285 | dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_OUT); |
| 286 | dm_gpio_set_value(&gpio_mii->mdio_gpio, v); |
| 287 | gpio_mii->mdio_value = v; |
| 288 | |
| 289 | return 0; |
| 290 | } |
| 291 | |
| 292 | static int mii_get_mdio(struct bb_miiphy_bus *bus, int *v) |
| 293 | { |
| 294 | struct gpio_mii *gpio_mii = bus->priv; |
| 295 | |
| 296 | dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN); |
| 297 | *v = (dm_gpio_get_value(&gpio_mii->mdio_gpio)); |
| 298 | |
| 299 | return 0; |
| 300 | } |
| 301 | |
| 302 | static int mii_set_mdc(struct bb_miiphy_bus *bus, int v) |
| 303 | { |
| 304 | struct gpio_mii *gpio_mii = bus->priv; |
| 305 | |
| 306 | dm_gpio_set_value(&gpio_mii->mdc_gpio, v); |
| 307 | |
| 308 | return 0; |
| 309 | } |
| 310 | |
| 311 | static int mii_delay(struct bb_miiphy_bus *bus) |
| 312 | { |
| 313 | udelay(1); |
| 314 | |
| 315 | return 0; |
| 316 | } |
| 317 | |
| 318 | struct bb_miiphy_bus bb_miiphy_buses[] = { |
| 319 | { |
| 320 | .name = "ihs0", |
| 321 | .init = mii_mdio_init, |
| 322 | .mdio_active = mii_mdio_active, |
| 323 | .mdio_tristate = mii_mdio_tristate, |
| 324 | .set_mdio = mii_set_mdio, |
| 325 | .get_mdio = mii_get_mdio, |
| 326 | .set_mdc = mii_set_mdc, |
| 327 | .delay = mii_delay, |
| 328 | .priv = &gpio_mii_set[0], |
| 329 | }, |
| 330 | { |
| 331 | .name = "ihs1", |
| 332 | .init = mii_mdio_init, |
| 333 | .mdio_active = mii_mdio_active, |
| 334 | .mdio_tristate = mii_mdio_tristate, |
| 335 | .set_mdio = mii_set_mdio, |
| 336 | .get_mdio = mii_get_mdio, |
| 337 | .set_mdc = mii_set_mdc, |
| 338 | .delay = mii_delay, |
| 339 | .priv = &gpio_mii_set[1], |
| 340 | }, |
| 341 | { |
| 342 | .name = "ihs2", |
| 343 | .init = mii_mdio_init, |
| 344 | .mdio_active = mii_mdio_active, |
| 345 | .mdio_tristate = mii_mdio_tristate, |
| 346 | .set_mdio = mii_set_mdio, |
| 347 | .get_mdio = mii_get_mdio, |
| 348 | .set_mdc = mii_set_mdc, |
| 349 | .delay = mii_delay, |
| 350 | .priv = &gpio_mii_set[2], |
| 351 | }, |
| 352 | }; |
| 353 | |
| 354 | int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); |