mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-22 11:13:07 +00:00
e24b58f5ed
Currently we require PHY interface mode to be known when finding/creating the PHY - the functions * phy_connect_phy_id() * phy_device_create() * create_phy_by_mask() * search_for_existing_phy() * get_phy_device_by_mask() * phy_find_by_mask() all require the interface parameter, but the only thing done with it is that it is assigned to phydev->interface. This makes it impossible to find a PHY device without overwriting the set mode. Since the interface mode is not used during .probe() and should be used at first in .config(), drop the interface parameter from these functions. Make the default value of phydev->interface (in phy_device_create()) to be PHY_INTERFACE_MODE_NA. Move the interface parameter to phy_connect_dev(), where it should be. Change all occurrences treewide. In occurrences where we don't call phy_connect_dev() for some reason (they only configure the PHY without connecting it to an ethernet controller), set phydev->interface = value from phy_find_by_mask call. Signed-off-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Ramon Fried <rfried.dev@gmail.com> Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
355 lines
7.8 KiB
C
355 lines
7.8 KiB
C
#include <common.h>
|
|
#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);
|