[U-Boot] [PATCH 5/7] driver: net: ti: introduce common mdio support library
Joe Hershberger
joe.hershberger at ni.com
Mon Oct 29 19:49:25 UTC 2018
On Thu, Oct 25, 2018 at 10:39 PM Grygorii Strashko
<grygorii.strashko at ti.com> wrote:
>
> All existing TI SoCs network HW have similar MDIO implementation, so
> introduce common mdio support library which can be reused by TI networking
> drivers.
>
> Signed-off-by: Grygorii Strashko <grygorii.strashko at ti.com>
> ---
> drivers/net/ti/Makefile | 2 +-
> drivers/net/ti/cpsw_mdio.c | 205 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/net/ti/cpsw_mdio.h | 18 ++++
> 3 files changed, 224 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ti/cpsw_mdio.c
> create mode 100644 drivers/net/ti/cpsw_mdio.h
>
> diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile
> index 4ab4a27..d2b6f20 100644
> --- a/drivers/net/ti/Makefile
> +++ b/drivers/net/ti/Makefile
> @@ -2,6 +2,6 @@
> #
> # Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>
> -obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o
> +obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o
> obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
> obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o
> diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c
> new file mode 100644
> index 0000000..ed9dd27
> --- /dev/null
> +++ b/drivers/net/ti/cpsw_mdio.c
> @@ -0,0 +1,205 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * CPSW MDIO generic driver for TI AMxx/K2x/EMAC devices.
> + *
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <miiphy.h>
> +
> +struct cpsw_mdio_regs {
> + u32 version;
> + u32 control;
> +#define CONTROL_IDLE BIT(31)
> +#define CONTROL_ENABLE BIT(30)
> +#define CONTROL_FAULT BIT(19)
> +#define CONTROL_FAULT_ENABLE BIT(18)
> +#define CONTROL_DIV_MASK GENMASK(15, 0)
> +
> + u32 alive;
> + u32 link;
> + u32 linkintraw;
> + u32 linkintmasked;
> + u32 __reserved_0[2];
> + u32 userintraw;
> + u32 userintmasked;
> + u32 userintmaskset;
> + u32 userintmaskclr;
> + u32 __reserved_1[20];
> +
> + struct {
> + u32 access;
> + u32 physel;
> +#define USERACCESS_GO BIT(31)
> +#define USERACCESS_WRITE BIT(30)
> +#define USERACCESS_ACK BIT(29)
> +#define USERACCESS_READ (0)
> +#define USERACCESS_PHY_REG_SHIFT (21)
> +#define USERACCESS_PHY_ADDR_SHIFT (16)
> +#define USERACCESS_DATA GENMASK(15, 0)
> + } user[0];
> +};
> +
> +#define CPSW_MDIO_DIV_DEF 0xff
> +#define PHY_REG_MASK 0x1f
> +#define PHY_ID_MASK 0x1f
> +
> +/*
> + * This timeout definition is a worst-case ultra defensive measure against
> + * unexpected controller lock ups. Ideally, we should never ever hit this
> + * scenario in practice.
> + */
> +#define CPSW_MDIO_TIMEOUT 100 /* msecs */
> +
> +struct cpsw_mdio {
> + struct cpsw_mdio_regs *regs;
> + struct mii_dev *bus;
> + int div;
> +};
> +
> +/* wait until hardware is ready for another user access */
> +static inline u32 cpsw_mdio_wait_for_user_access(struct cpsw_mdio *mdio)
> +{
> + u32 reg = 0;
> + int timeout = CPSW_MDIO_TIMEOUT;
> +
> + while (timeout-- && ((reg = readl(&mdio->regs->user[0].access))
> + & USERACCESS_GO))
> + udelay(10);
Seems we should be using wait_for_bit instead of this function.
> +
> + if (timeout == -1) {
> + printf("wait_for_user_access Timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + return reg;
> +}
> +
> +static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
> + int dev_addr, int phy_reg)
> +{
> + struct cpsw_mdio *mdio = bus->priv;
> + int data;
> + u32 reg;
> +
> + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
> + return -EINVAL;
> +
> + cpsw_mdio_wait_for_user_access(mdio);
> + reg = (USERACCESS_GO | USERACCESS_READ |
> + (phy_reg << USERACCESS_PHY_REG_SHIFT) |
> + (phy_id << USERACCESS_PHY_ADDR_SHIFT));
> + writel(reg, &mdio->regs->user[0].access);
> + reg = cpsw_mdio_wait_for_user_access(mdio);
> +
> + data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
> + return data;
> +}
> +
> +static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr,
> + int phy_reg, u16 data)
> +{
> + struct cpsw_mdio *mdio = bus->priv;
> + u32 reg;
> +
> + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
> + return -EINVAL;
> +
> + cpsw_mdio_wait_for_user_access(mdio);
> + reg = (USERACCESS_GO | USERACCESS_WRITE |
> + (phy_reg << USERACCESS_PHY_REG_SHIFT) |
> + (phy_id << USERACCESS_PHY_ADDR_SHIFT) |
> + (data & USERACCESS_DATA));
> + writel(reg, &mdio->regs->user[0].access);
> + cpsw_mdio_wait_for_user_access(mdio);
> +
> + return 0;
> +}
> +
> +u32 cpsw_mdio_get_alive(struct mii_dev *bus)
> +{
> + struct cpsw_mdio *mdio = bus->priv;
> + u32 val;
> +
> + val = readl(&mdio->regs->control);
> + return val & GENMASK(15, 0);
> +}
> +
> +struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base,
> + u32 bus_freq, int fck_freq)
> +{
> + struct cpsw_mdio *cpsw_mdio;
> + int ret;
> +
> + cpsw_mdio = calloc(1, sizeof(*cpsw_mdio));
> + if (!cpsw_mdio) {
> + debug("failed to alloc cpsw_mdio\n");
> + return NULL;
> + }
> +
> + cpsw_mdio->bus = mdio_alloc();
> + if (!cpsw_mdio->bus) {
> + debug("failed to alloc mii bus\n");
> + free(cpsw_mdio);
> + return NULL;
> + }
> +
> + cpsw_mdio->regs = (struct cpsw_mdio_regs *)mdio_base;
> +
> + if (!bus_freq || !fck_freq)
> + cpsw_mdio->div = CPSW_MDIO_DIV_DEF;
> + else
> + cpsw_mdio->div = (fck_freq / bus_freq) - 1;
> + cpsw_mdio->div &= CONTROL_DIV_MASK;
> +
> + /* set enable and clock divider */
> + writel(cpsw_mdio->div | CONTROL_ENABLE | CONTROL_FAULT |
> + CONTROL_FAULT_ENABLE, &cpsw_mdio->regs->control);
> + while (readl(&cpsw_mdio->regs->control) & CONTROL_IDLE)
> + ;
> +
> + /*
> + * wait for scan logic to settle:
> + * the scan time consists of (a) a large fixed component, and (b) a
> + * small component that varies with the mii bus frequency. These
> + * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
> + * silicon. Since the effect of (b) was found to be largely
> + * negligible, we keep things simple here.
> + */
> + udelay(1000);
mdelay(1);
> +
> + cpsw_mdio->bus->read = cpsw_mdio_read;
> + cpsw_mdio->bus->write = cpsw_mdio_write;
> + cpsw_mdio->bus->priv = cpsw_mdio;
> + strcpy(cpsw_mdio->bus->name, name);
snprintf(cpsw_mdio->bus->name, sizeof(cpsw_mdio->bus->name), name);
> +
> + ret = mdio_register(cpsw_mdio->bus);
> + if (ret < 0) {
> + debug("failed to register mii bus\n");
> + goto free_bus;
> + }
> +
> + return cpsw_mdio->bus;
> +
> +free_bus:
> + free(cpsw_mdio->bus);
mdio_free(cpsw_mdio->bus);
> + free(cpsw_mdio);
> + return NULL;
> +}
> +
> +void cpsw_mdio_free(struct mii_dev *bus)
> +{
> + struct cpsw_mdio *mdio = bus->priv;
> + u32 reg;
> +
> + /* disable mdio */
> + reg = readl(&mdio->regs->control);
> + reg &= ~CONTROL_ENABLE;
> + writel(reg, &mdio->regs->control);
> +
> + mdio_unregister(bus);
> + mdio_free(bus);
> + free(mdio);
> +}
> diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h
> new file mode 100644
> index 0000000..4a76d4e
> --- /dev/null
> +++ b/drivers/net/ti/cpsw_mdio.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * CPSW MDIO generic driver API for TI AMxx/K2x/EMAC devices.
> + *
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> + */
> +
> +#ifndef CPSW_MDIO_H_
> +#define CPSW_MDIO_H_
> +
> +struct cpsw_mdio;
> +
> +struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base,
> + u32 bus_freq, int fck_freq);
> +void cpsw_mdio_free(struct mii_dev *bus);
> +u32 cpsw_mdio_get_alive(struct mii_dev *bus);
> +
> +#endif /* CPSW_MDIO_H_ */
> --
> 2.10.5
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot
More information about the U-Boot
mailing list