diff --git a/sys/dev/enetc/enetc_mdio.h b/sys/dev/enetc/enetc_mdio.h new file mode 100644 --- /dev/null +++ b/sys/dev/enetc/enetc_mdio.h @@ -0,0 +1,58 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Alstom Group. + * Copyright (c) 2021 Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ENETC_MDIO_H_ +#define _ENETC_MDIO_H_ + +/* MDIO registers */ +#define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */ +#define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8) + +#define ENETC_MDIO_CTL 0x4 /* MDIO control */ +#define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f) +#define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5) + +#define ENETC_MDIO_DATA 0x8 /* MDIO data */ +#define MDIO_DATA(x) ((x) & 0xffff) + +#define ENETC_MDIO_ADDR 0xc /* MDIO address */ + +#define MDIO_CFG_BSY BIT(0) +#define MDIO_CFG_RD_ER BIT(1) +#define MDIO_CFG_ENC45 BIT(6) +#define MDIO_CFG_NEG BIT(23) +#define MDIO_CTL_READ BIT(15) +#define MII_ADDR_C45 BIT(30) + +/* MDIO configuration and helpers */ +#define ENETC_MDC_DIV 258 +#define ENETC_TIMEOUT 1000 + +int enetc_mdio_write(struct resource*, int, int, int, int); +int enetc_mdio_read(struct resource*, int, int, int); + +#endif diff --git a/sys/dev/enetc/enetc_mdio.c b/sys/dev/enetc/enetc_mdio.c new file mode 100644 --- /dev/null +++ b/sys/dev/enetc/enetc_mdio.c @@ -0,0 +1,155 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Alstom Group. + * Copyright (c) 2021 Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define ENETC_MDIO_RD4(regs, base, off) \ + bus_read_4((regs), (base) + (off)) +#define ENETC_MDIO_WR4(regs, base, off, value) \ + bus_write_4((regs), (base) + (off), (value)) + +static int +enetc_mdio_wait(struct resource *regs, int mdio_base) +{ + int i; + uint32_t val; + + i = 0; + do { + DELAY(100); + val = ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_CFG); + if ((val & MDIO_CFG_BSY) == 0) + return (0); + } while (i++ < ENETC_TIMEOUT); + + return (ETIMEDOUT); +} + +int +enetc_mdio_read(struct resource *regs, int mdio_base, int phy, int reg) +{ + uint32_t mdio_cfg, mdio_ctl; + uint16_t dev_addr; + + mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; + if (reg & MII_ADDR_C45) { + /* clause 45 */ + dev_addr = (reg >> 16) & 0x1f; + mdio_cfg |= MDIO_CFG_ENC45; + } else { + /* clause 22 */ + dev_addr = reg & 0x1f; + mdio_cfg &= ~MDIO_CFG_ENC45; + } + + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CFG, mdio_cfg); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + + /* Set port and device addr. */ + mdio_ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(dev_addr); + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl); + + /* Set the register address. */ + if (reg & MII_ADDR_C45) { + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_ADDR, reg & 0xffff); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + } + + /* Initiate the read. */ + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + + /* Check if any error occurred while reading PHY register. */ + if (ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) + return (ENXIO); + + return (MDIO_DATA(ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_DATA))); +} + +int +enetc_mdio_write(struct resource *regs, int mdio_base, int phy, int reg, + int data) +{ + uint32_t mdio_cfg, mdio_ctl; + uint16_t dev_addr; + + mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; + if (reg & MII_ADDR_C45) { + /* clause 45 */ + dev_addr = (reg >> 16) & 0x1f; + mdio_cfg |= MDIO_CFG_ENC45; + } else { + /* clause 22 */ + dev_addr = reg & 0x1f; + mdio_cfg &= ~MDIO_CFG_ENC45; + } + + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CFG, mdio_cfg); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + + /* Set port and device addr. */ + mdio_ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(dev_addr); + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl); + + /* Set the register address. */ + if (reg & MII_ADDR_C45) { + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_ADDR, reg & 0xffff); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + } + + /* Write the value. */ + ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_DATA, MDIO_DATA(data)); + + if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT) + return (EIO); + + return (0); +}