mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-25 20:43:32 +00:00
61efbece57
strncpy() simply bails out when copying a source string whose size exceeds the destination string size, potentially leaving the destination string unterminated. One possible way to address is to pass MDIO_NAME_LEN - 1 and a previously zero-initialized destination string, but this is more difficult to maintain. The chosen alternative is to use strlcpy(), which properly limits the copy len in the (srclen >= size) case to "size - 1", and which is also more efficient than the strncpy() byte-by-byte implementation by using memcpy. The destination string returned by strlcpy() is always NULL terminated. Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Reviewed-by: Ramon Fried <rfried.dev@gmail.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;
|
|
|
|
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);
|