Index: sys/arm/conf/std.qca =================================================================== --- sys/arm/conf/std.qca +++ sys/arm/conf/std.qca @@ -72,3 +72,11 @@ device dwc3 device qcom_dwc3 options USB_HOST_ALIGN=64 + +# Ethernet support +device mdio +device mii +device miibus +device qcom_mdio_ipq4018 +device etherswitch +device ar40xx_switch Index: sys/arm/qualcomm/std.ipq4018 =================================================================== --- sys/arm/qualcomm/std.ipq4018 +++ sys/arm/qualcomm/std.ipq4018 @@ -6,6 +6,27 @@ arm/qualcomm/ipq4018_usb_hs_phy.c optional qcom_ipq4018_hs_usbphy arm/qualcomm/ipq4018_usb_ss_phy.c optional qcom_ipq4018_ss_usbphy +dev/etherswitch/ar40xx/ar40xx_main.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_phy.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_atu.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_port.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_mib.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_mirror.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_vtu.c \ + optional mdio etherswitch ar40xx_switch +dev/etherswitch/ar40xx/ar40xx_hw_mdio.c \ + optional mdio etherswitch ar40xx_switch + dev/qcom_dwc3/qcom_dwc3.c optional qcom_dwc3 dev/qcom_rnd/qcom_rnd.c optional qcom_rnd @@ -32,3 +53,5 @@ dev/qcom_tlmm/qcom_tlmm_pinmux.c optional qcom_tlmm_ipq4018 dev/qcom_tcsr/qcom_tcsr.c optional qcom_tcsr + +dev/qcom_mdio/qcom_mdio_ipq4018.c optional qcom_mdio_ipq4018 Index: sys/dev/etherswitch/ar40xx/ar40xx_debug.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_debug.h @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef __AR40XX_DEBUG_H__ +#define __AR40XX_DEBUG_H__ + +#define AR40XX_DBG_HW_INIT 0x00000001 +#define AR40XX_DBG_HW_RESET 0x00000002 +#define AR40XX_DBG_HW_PORT_INIT 0x00000004 +#define AR40XX_DBG_VTU_OP 0x00000008 +#define AR40XX_DBG_ATU_OP 0x00000010 +#define AR40XX_DBG_PORT_STATUS 0x00000020 + +#define AR40XX_DPRINTF(sc, flags, ...) \ + do { \ + if ((sc)->sc_debug & (flags)) \ + device_printf((sc)->sc_dev, __VA_ARGS__); \ + } while (0) + +#endif /* __AR40XX_DEBUG_H__ */ Index: sys/dev/etherswitch/ar40xx/ar40xx_hw.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw.h @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_H__ +#define __AR40XX_HW_H__ + +extern int ar40xx_hw_ess_reset(struct ar40xx_softc *sc); +extern int ar40xx_hw_init_globals(struct ar40xx_softc *sc); +extern int ar40xx_hw_vlan_init(struct ar40xx_softc *sc); +extern int ar40xx_hw_sw_hw_apply(struct ar40xx_softc *sc); +extern int ar40xx_hw_wait_bit(struct ar40xx_softc *sc, int reg, + uint32_t mask, uint32_t val); +extern int ar40xx_hw_read_switch_mac_address(struct ar40xx_softc *sc, + struct ether_addr *ea); +extern int ar40xx_hw_write_switch_mac_address(struct ar40xx_softc *sc, + struct ether_addr *ea); + +#endif /* __AR40XX_HW_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw.c @@ -0,0 +1,359 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* + * XXX these are here for now; move the code using these + * into main.c once this is all done! + */ +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +/* + * Reset the ESS switch. This also resets the ESS ethernet + * and PSGMII block. + */ +int +ar40xx_hw_ess_reset(struct ar40xx_softc *sc) +{ + int ret; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_RESET, "%s: called\n", __func__); + + ret = hwreset_assert(sc->sc_ess_rst); + if (ret != 0) { + device_printf(sc->sc_dev, "ERROR: failed to assert reset\n"); + return ret; + } + DELAY(10*1000); + + ret = hwreset_deassert(sc->sc_ess_rst); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to deassert reset\n"); + return ret; + } + + DELAY(10*1000); + + return (0); +} + +int +ar40xx_hw_init_globals(struct ar40xx_softc *sc) +{ + uint32_t reg; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); + + /* enable CPU port and disable mirror port */ + reg = AR40XX_FWD_CTRL0_CPU_PORT_EN + | AR40XX_FWD_CTRL0_MIRROR_PORT; + AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); + + /* forward multicast and broadcast frames to CPU */ + reg = (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_UC_FLOOD_S) + | (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_MC_FLOOD_S) + | (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_BC_FLOOD_S); + AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL1, reg); + + /* enable jumbo frames */ + reg = AR40XX_REG_READ(sc, AR40XX_REG_MAX_FRAME_SIZE); + reg &= ~AR40XX_MAX_FRAME_SIZE_MTU; + reg |= 9018 + 8 + 2; + AR40XX_REG_WRITE(sc, AR40XX_REG_MAX_FRAME_SIZE, reg); + + /* Enable MIB counters */ + reg = AR40XX_REG_READ(sc, AR40XX_REG_MODULE_EN); + reg |= AR40XX_MODULE_EN_MIB; + AR40XX_REG_WRITE(sc, AR40XX_REG_MODULE_EN, reg); + + /* Disable AZ */ + AR40XX_REG_WRITE(sc, AR40XX_REG_EEE_CTRL, 0); + + /* set flowctrl thershold for cpu port */ + reg = (AR40XX_PORT0_FC_THRESH_ON_DFLT << 16) + | AR40XX_PORT0_FC_THRESH_OFF_DFLT; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(0), reg); + + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +int +ar40xx_hw_vlan_init(struct ar40xx_softc *sc) +{ + int i; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); + + /* Enable VLANs by default */ + sc->sc_vlan.vlan = 1; + + /* Configure initial LAN/WAN bitmap and include CPU port as tagged */ + sc->sc_vlan.vlan_id[AR40XX_LAN_VLAN] = AR40XX_LAN_VLAN + | ETHERSWITCH_VID_VALID; + sc->sc_vlan.vlan_id[AR40XX_WAN_VLAN] = AR40XX_WAN_VLAN + | ETHERSWITCH_VID_VALID; + + sc->sc_vlan.vlan_ports[AR40XX_LAN_VLAN] = + sc->sc_config.switch_cpu_bmp | sc->sc_config.switch_lan_bmp; + sc->sc_vlan.vlan_untagged[AR40XX_LAN_VLAN] = + sc->sc_config.switch_lan_bmp; + + sc->sc_vlan.vlan_ports[AR40XX_WAN_VLAN] = + sc->sc_config.switch_cpu_bmp | sc->sc_config.switch_wan_bmp; + sc->sc_vlan.vlan_untagged[AR40XX_WAN_VLAN] = + sc->sc_config.switch_wan_bmp; + + /* Populate the per-port PVID - pvid[] is an index into vlan_id[] */ + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + if (sc->sc_config.switch_lan_bmp & (1U << i)) + sc->sc_vlan.pvid[i] = AR40XX_LAN_VLAN; + if (sc->sc_config.switch_wan_bmp & (1U << i)) + sc->sc_vlan.pvid[i] = AR40XX_WAN_VLAN; + } + + return (0); +} + +/* + * Apply the per-port and global configuration from software. + * + * This is useful if we ever start doing the linux switch framework + * thing of updating the config in one hit and pushing it to the + * hardware. For now it's just used in the reset path. + */ +int +ar40xx_hw_sw_hw_apply(struct ar40xx_softc *sc) +{ + uint8_t portmask[AR40XX_NUM_PORTS]; + int i, j, ret; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); + + /* + * Flush the VTU configuration. + */ + ret = ar40xx_hw_vtu_flush(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: couldn't apply config; vtu flush failed (%d)\n", + ret); + return (ret); + } + + memset(portmask, 0, sizeof(portmask)); + + /* + * Configure the ports based on whether it's 802.1q + * VLANs, or just straight up per-port VLANs. + */ + if (sc->sc_vlan.vlan) { + device_printf(sc->sc_dev, "%s: configuring 802.1q VLANs\n", + __func__); + for (j = 0; j < AR40XX_NUM_VTU_ENTRIES; j++) { + uint8_t vp = sc->sc_vlan.vlan_ports[j]; + + if (!vp) + continue; + if ((sc->sc_vlan.vlan_id[j] + & ETHERSWITCH_VID_VALID) == 0) + continue; + + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + uint8_t mask = (1U << i); + + if (vp & mask) + portmask[i] |= vp & ~mask; + } + + ar40xx_hw_vtu_load_vlan(sc, + sc->sc_vlan.vlan_id[j] & ETHERSWITCH_VID_MASK, + sc->sc_vlan.vlan_ports[j], + sc->sc_vlan.vlan_untagged[j]); + } + } else { + device_printf(sc->sc_dev, "%s: configuring per-port VLANs\n", + __func__); + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + if (i == AR40XX_PORT_CPU) + continue; + + portmask[i] = (1U << AR40XX_PORT_CPU); + portmask[AR40XX_PORT_CPU] |= (1U << i); + } + } + + /* + * Update per-port destination mask, vlan tag settings + */ + for (i = 0; i < AR40XX_NUM_PORTS; i++) + (void) ar40xx_hw_port_setup(sc, i, portmask[i]); + + /* Set the mirror register config */ + ret = ar40xx_hw_mirror_set_registers(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: couldn't apply config; mirror config failed" + " (%d)\n", + ret); + return (ret); + } + + return (0); +} + +int +ar40xx_hw_wait_bit(struct ar40xx_softc *sc, int reg, uint32_t mask, + uint32_t val) +{ + int timeout = 20; + uint32_t t; + + while (true) { + AR40XX_REG_BARRIER_READ(sc); + t = AR40XX_REG_READ(sc, reg); + if ((t & mask) == val) + return 0; + + if (timeout-- <= 0) + break; + + DELAY(20); + } + + device_printf(sc->sc_dev, "ERROR: timeout for reg " + "%08x: %08x & %08x != %08x\n", + (unsigned int)reg, t, mask, val); + return (ETIMEDOUT); +} + +/* + * Read the switch MAC address. + */ +int +ar40xx_hw_read_switch_mac_address(struct ar40xx_softc *sc, + struct ether_addr *ea) +{ + uint32_t ret0, ret1; + char *s; + + s = (void *) ea; + + AR40XX_LOCK_ASSERT(sc); + + AR40XX_REG_BARRIER_READ(sc); + ret0 = AR40XX_REG_READ(sc, AR40XX_REG_SW_MAC_ADDR0); + ret1 = AR40XX_REG_READ(sc, AR40XX_REG_SW_MAC_ADDR1); + + s[5] = MS(ret0, AR40XX_REG_SW_MAC_ADDR0_BYTE5); + s[4] = MS(ret0, AR40XX_REG_SW_MAC_ADDR0_BYTE4); + s[3] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE3); + s[2] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE2); + s[1] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE1); + s[0] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE0); + + return (0); +} + +/* + * Set the switch MAC address. + */ +int +ar40xx_hw_write_switch_mac_address(struct ar40xx_softc *sc, + struct ether_addr *ea) +{ + uint32_t ret0 = 0, ret1 = 0; + char *s; + + s = (void *) ea; + + AR40XX_LOCK_ASSERT(sc); + + ret0 |= SM(s[5], AR40XX_REG_SW_MAC_ADDR0_BYTE5); + ret0 |= SM(s[4], AR40XX_REG_SW_MAC_ADDR0_BYTE4); + + ret1 |= SM(s[3], AR40XX_REG_SW_MAC_ADDR1_BYTE3); + ret1 |= SM(s[2], AR40XX_REG_SW_MAC_ADDR1_BYTE2); + ret1 |= SM(s[1], AR40XX_REG_SW_MAC_ADDR1_BYTE1); + ret1 |= SM(s[0], AR40XX_REG_SW_MAC_ADDR1_BYTE0); + + AR40XX_REG_WRITE(sc, AR40XX_REG_SW_MAC_ADDR0, ret0); + AR40XX_REG_WRITE(sc, AR40XX_REG_SW_MAC_ADDR1, ret1); + + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.h @@ -0,0 +1,39 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_ATU_H__ +#define __AR40XX_HW_ATU_H__ + +extern int ar40xx_hw_atu_wait_busy(struct ar40xx_softc *sc); +extern int ar40xx_hw_atu_flush_all(struct ar40xx_softc *sc); +extern int ar40xx_hw_atu_flush_port(struct ar40xx_softc *sc, int port); +extern int ar40xx_hw_atu_fetch_entry(struct ar40xx_softc *sc, + etherswitch_atu_entry_t *e, int atu_fetch_op); + +#endif /* __AR40XX_HW_ATU_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c @@ -0,0 +1,218 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +int +ar40xx_hw_atu_wait_busy(struct ar40xx_softc *sc) +{ + int ret; + + ret = ar40xx_hw_wait_bit(sc, AR40XX_REG_ATU_FUNC, + AR40XX_ATU_FUNC_BUSY, 0); + return (ret); +} + +int +ar40xx_hw_atu_flush_all(struct ar40xx_softc *sc) +{ + int ret; + + AR40XX_LOCK_ASSERT(sc); + + AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: called\n", __func__); + ret = ar40xx_hw_atu_wait_busy(sc); + if (ret != 0) + return (ret); + + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, + AR40XX_ATU_FUNC_OP_FLUSH + | AR40XX_ATU_FUNC_BUSY); + AR40XX_REG_BARRIER_WRITE(sc); + + return (ret); +} + +int +ar40xx_hw_atu_flush_port(struct ar40xx_softc *sc, int port) +{ + uint32_t val; + int ret; + + AR40XX_LOCK_ASSERT(sc); + + AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: called, port=%d\n", + __func__, port); + + if (port >= AR40XX_NUM_PORTS) { + return (EINVAL); + } + + ret = ar40xx_hw_atu_wait_busy(sc); + if (ret != 0) + return (ret); + + val = AR40XX_ATU_FUNC_OP_FLUSH_UNICAST; + val |= (port << AR40XX_ATU_FUNC_PORT_NUM_S) + & AR40XX_ATU_FUNC_PORT_NUM; + + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, + val | AR40XX_ATU_FUNC_BUSY); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +int +ar40xx_hw_atu_fetch_entry(struct ar40xx_softc *sc, etherswitch_atu_entry_t *e, + int atu_fetch_op) +{ + uint32_t ret0, ret1, ret2, val; + int ret; + + AR40XX_LOCK_ASSERT(sc); + + switch (atu_fetch_op) { + case 0: + /* Initialise things for the first fetch */ + + AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, + "%s: initializing\n", __func__); + + ret = ar40xx_hw_atu_wait_busy(sc); + if (ret != 0) + return (ret); + + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, + AR40XX_ATU_FUNC_OP_GET_NEXT); + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA0, 0); + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA1, 0); + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA2, 0); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); + case 1: + AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, + "%s: reading next\n", __func__); + /* + * Attempt to read the next address entry; don't modify what + * is there in these registers as its used for the next fetch + */ + ret = ar40xx_hw_atu_wait_busy(sc); + if (ret != 0) + return (ret); + + /* Begin the next read event; not modifying anything */ + AR40XX_REG_BARRIER_READ(sc); + val = AR40XX_REG_READ(sc, AR40XX_REG_ATU_FUNC); + val |= AR40XX_ATU_FUNC_BUSY; + AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, val); + AR40XX_REG_BARRIER_WRITE(sc); + + /* Wait for it to complete */ + ret = ar40xx_hw_atu_wait_busy(sc); + if (ret != 0) + return (ret); + + /* Fetch the ethernet address and ATU status */ + AR40XX_REG_BARRIER_READ(sc); + ret0 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA0); + ret1 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA1); + ret2 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA2); + + /* If the status is zero, then we're done */ + if (MS(ret2, AR40XX_ATU_FUNC_DATA2_STATUS) == 0) + return (ENOENT); + + /* MAC address */ + e->es_macaddr[5] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR3); + e->es_macaddr[4] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR2); + e->es_macaddr[3] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR1); + e->es_macaddr[2] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR0); + e->es_macaddr[0] = MS(ret1, AR40XX_ATU_DATA1_MAC_ADDR5); + e->es_macaddr[1] = MS(ret1, AR40XX_ATU_DATA1_MAC_ADDR4); + + /* Bitmask of ports this entry is for */ + e->es_portmask = MS(ret1, AR40XX_ATU_DATA1_DEST_PORT); + + /* TODO: other flags that are interesting */ + + AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, + "%s: MAC %6D portmask 0x%08x\n", + __func__, + e->es_macaddr, ":", e->es_portmask); + return (0); + default: + return (EINVAL); + } + return (EINVAL); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_MDIO_H__ +#define __AR40XX_HW_MDIO_H__ + +extern int ar40xx_hw_phy_dbg_write(struct ar40xx_softc *sc, int phy, + uint16_t dbg, uint16_t data); +extern int ar40xx_hw_phy_dbg_read(struct ar40xx_softc *sc, int phy, + uint16_t dbg); +extern int ar40xx_hw_phy_mmd_write(struct ar40xx_softc *sc, uint32_t phy_id, + uint16_t mmd_num, uint16_t reg_id, uint16_t reg_val); +extern int ar40xx_hw_phy_mmd_read(struct ar40xx_softc *sc, uint32_t phy_id, + uint16_t mmd_num, uint16_t reg_id); + +#endif /* __AR40XX_HW_MDIO_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c @@ -0,0 +1,131 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +int +ar40xx_hw_phy_dbg_write(struct ar40xx_softc *sc, int phy, uint16_t dbg, + uint16_t data) +{ + AR40XX_LOCK_ASSERT(sc); + device_printf(sc->sc_dev, "%s: TODO\n", __func__); + return (0); +} + +int +ar40xx_hw_phy_dbg_read(struct ar40xx_softc *sc, int phy, uint16_t dbg) +{ + AR40XX_LOCK_ASSERT(sc); + device_printf(sc->sc_dev, "%s: TODO\n", __func__); + return (-1); +} + +int +ar40xx_hw_phy_mmd_write(struct ar40xx_softc *sc, uint32_t phy_id, + uint16_t mmd_num, uint16_t reg_id, uint16_t reg_val) +{ + + AR40XX_LOCK_ASSERT(sc); + + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, + mmd_num); + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, + reg_id); + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, + 0x4000 | mmd_num); + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, + reg_val); + + return (0); +} + +int +ar40xx_hw_phy_mmd_read(struct ar40xx_softc *sc, uint32_t phy_id, + uint16_t mmd_num, uint16_t reg_id) +{ + uint16_t value; + + AR40XX_LOCK_ASSERT(sc); + + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, + mmd_num); + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, + reg_id); + MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, + 0x4000 | mmd_num); + + value = MDIO_READREG(sc->sc_mdio_dev, phy_id, + AR40XX_MII_ATH_MMD_DATA); + + return value; +} + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.h @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_MIB_H__ +#define __AR40XX_HW_MIB_H__ + +extern int ar40xx_hw_mib_op(struct ar40xx_softc *sc, uint32_t op); +extern int ar40xx_hw_mib_capture(struct ar40xx_softc *sc); +extern int ar40xx_hw_mib_flush(struct ar40xx_softc *sc); + +extern int ar40xx_hw_mib_fetch(struct ar40xx_softc *sc, int port); + +#endif /* __AR40XX_HW_MIB_H__ */ Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c @@ -0,0 +1,196 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +#define MIB_DESC(_s , _o, _n) \ + { \ + .size = (_s), \ + .offset = (_o), \ + .name = (_n), \ + } + +static const struct ar40xx_mib_desc ar40xx_mibs[] = { + MIB_DESC(1, AR40XX_STATS_RXBROAD, "RxBroad"), + MIB_DESC(1, AR40XX_STATS_RXPAUSE, "RxPause"), + MIB_DESC(1, AR40XX_STATS_RXMULTI, "RxMulti"), + MIB_DESC(1, AR40XX_STATS_RXFCSERR, "RxFcsErr"), + MIB_DESC(1, AR40XX_STATS_RXALIGNERR, "RxAlignErr"), + MIB_DESC(1, AR40XX_STATS_RXRUNT, "RxRunt"), + MIB_DESC(1, AR40XX_STATS_RXFRAGMENT, "RxFragment"), + MIB_DESC(1, AR40XX_STATS_RX64BYTE, "Rx64Byte"), + MIB_DESC(1, AR40XX_STATS_RX128BYTE, "Rx128Byte"), + MIB_DESC(1, AR40XX_STATS_RX256BYTE, "Rx256Byte"), + MIB_DESC(1, AR40XX_STATS_RX512BYTE, "Rx512Byte"), + MIB_DESC(1, AR40XX_STATS_RX1024BYTE, "Rx1024Byte"), + MIB_DESC(1, AR40XX_STATS_RX1518BYTE, "Rx1518Byte"), + MIB_DESC(1, AR40XX_STATS_RXMAXBYTE, "RxMaxByte"), + MIB_DESC(1, AR40XX_STATS_RXTOOLONG, "RxTooLong"), + MIB_DESC(2, AR40XX_STATS_RXGOODBYTE, "RxGoodByte"), + MIB_DESC(2, AR40XX_STATS_RXBADBYTE, "RxBadByte"), + MIB_DESC(1, AR40XX_STATS_RXOVERFLOW, "RxOverFlow"), + MIB_DESC(1, AR40XX_STATS_FILTERED, "Filtered"), + MIB_DESC(1, AR40XX_STATS_TXBROAD, "TxBroad"), + MIB_DESC(1, AR40XX_STATS_TXPAUSE, "TxPause"), + MIB_DESC(1, AR40XX_STATS_TXMULTI, "TxMulti"), + MIB_DESC(1, AR40XX_STATS_TXUNDERRUN, "TxUnderRun"), + MIB_DESC(1, AR40XX_STATS_TX64BYTE, "Tx64Byte"), + MIB_DESC(1, AR40XX_STATS_TX128BYTE, "Tx128Byte"), + MIB_DESC(1, AR40XX_STATS_TX256BYTE, "Tx256Byte"), + MIB_DESC(1, AR40XX_STATS_TX512BYTE, "Tx512Byte"), + MIB_DESC(1, AR40XX_STATS_TX1024BYTE, "Tx1024Byte"), + MIB_DESC(1, AR40XX_STATS_TX1518BYTE, "Tx1518Byte"), + MIB_DESC(1, AR40XX_STATS_TXMAXBYTE, "TxMaxByte"), + MIB_DESC(1, AR40XX_STATS_TXOVERSIZE, "TxOverSize"), + MIB_DESC(2, AR40XX_STATS_TXBYTE, "TxByte"), + MIB_DESC(1, AR40XX_STATS_TXCOLLISION, "TxCollision"), + MIB_DESC(1, AR40XX_STATS_TXABORTCOL, "TxAbortCol"), + MIB_DESC(1, AR40XX_STATS_TXMULTICOL, "TxMultiCol"), + MIB_DESC(1, AR40XX_STATS_TXSINGLECOL, "TxSingleCol"), + MIB_DESC(1, AR40XX_STATS_TXEXCDEFER, "TxExcDefer"), + MIB_DESC(1, AR40XX_STATS_TXDEFER, "TxDefer"), + MIB_DESC(1, AR40XX_STATS_TXLATECOL, "TxLateCol"), +}; + + +int +ar40xx_hw_mib_op(struct ar40xx_softc *sc, uint32_t op) +{ + uint32_t reg; + int ret; + + AR40XX_LOCK_ASSERT(sc); + + /* Trigger capturing statistics on all ports */ + AR40XX_REG_BARRIER_READ(sc); + reg = AR40XX_REG_READ(sc, AR40XX_REG_MIB_FUNC); + reg &= ~AR40XX_MIB_FUNC; + reg |= (op << AR40XX_MIB_FUNC_S); + AR40XX_REG_WRITE(sc, AR40XX_REG_MIB_FUNC, reg); + AR40XX_REG_BARRIER_WRITE(sc); + + /* Now wait */ + ret = ar40xx_hw_wait_bit(sc, AR40XX_REG_MIB_FUNC, + AR40XX_MIB_BUSY, 0); + if (ret != 0) { + device_printf(sc->sc_dev, + "%s: ERROR: timeout waiting for MIB load\n", + __func__); + } + + return ret; +} + +int +ar40xx_hw_mib_capture(struct ar40xx_softc *sc) +{ + int ret; + + ret = ar40xx_hw_mib_op(sc, AR40XX_MIB_FUNC_CAPTURE); + return (ret); +} + +int +ar40xx_hw_mib_flush(struct ar40xx_softc *sc) +{ + int ret; + + ret = ar40xx_hw_mib_op(sc, AR40XX_MIB_FUNC_FLUSH); + return (ret); +} + +int +ar40xx_hw_mib_fetch(struct ar40xx_softc *sc, int port) +{ + uint64_t val; + uint32_t base, reg; + int i; + + base = AR40XX_REG_PORT_STATS_START + + (AR40XX_REG_PORT_STATS_LEN * port); + + /* For now just print them out, we'll store them later */ + AR40XX_REG_BARRIER_READ(sc); + for (i = 0; i < nitems(ar40xx_mibs); i++) { + val = 0; + + val = AR40XX_REG_READ(sc, base + ar40xx_mibs[i].offset); + if (ar40xx_mibs[i].size == 2) { + reg = AR40XX_REG_READ(sc, base + ar40xx_mibs[i].offset + 4); + val |= ((uint64_t) reg << 32); + } + + device_printf(sc->sc_dev, "%s[%d] = %llu\n", ar40xx_mibs[i].name, port, val); + } + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.h @@ -0,0 +1,35 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_MIRROR_H__ +#define __AR40XX_HW_MIRROR_H__ + +extern int ar40xx_hw_mirror_set_registers(struct ar40xx_softc *sc); + +#endif /* __AR40XX_HW_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c @@ -0,0 +1,134 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +int +ar40xx_hw_mirror_set_registers(struct ar40xx_softc *sc) +{ + uint32_t reg; + int port; + + /* Reset the mirror registers before configuring */ + reg = AR40XX_REG_READ(sc, AR40XX_REG_FWD_CTRL0); + reg &= ~(AR40XX_FWD_CTRL0_MIRROR_PORT); + reg |= (0xF << AR40XX_FWD_CTRL0_MIRROR_PORT_S); + AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); + AR40XX_REG_BARRIER_WRITE(sc); + + for (port = 0; port < AR40XX_NUM_PORTS; port++) { + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(port)); + reg &= ~AR40XX_PORT_LOOKUP_ING_MIRROR_EN; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); + + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(port)); + reg &= ~AR40XX_PORT_HOL_CTRL1_EG_MIRROR_EN; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HOL_CTRL1(port), reg); + + AR40XX_REG_BARRIER_WRITE(sc); + } + + /* Now, enable mirroring if requested */ + if (sc->sc_monitor.source_port >= AR40XX_NUM_PORTS + || sc->sc_monitor.monitor_port >= AR40XX_NUM_PORTS + || sc->sc_monitor.source_port == sc->sc_monitor.monitor_port) { + return (0); + } + + reg = AR40XX_REG_READ(sc, AR40XX_REG_FWD_CTRL0); + reg &= ~AR40XX_FWD_CTRL0_MIRROR_PORT; + reg |= + (sc->sc_monitor.monitor_port << AR40XX_FWD_CTRL0_MIRROR_PORT_S); + AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); + + if (sc->sc_monitor.mirror_rx) { + reg = AR40XX_REG_READ(sc, + AR40XX_REG_PORT_LOOKUP(sc->sc_monitor.source_port)); + reg |= AR40XX_PORT_LOOKUP_ING_MIRROR_EN; + AR40XX_REG_WRITE(sc, + AR40XX_REG_PORT_LOOKUP(sc->sc_monitor.source_port), + reg); + AR40XX_REG_BARRIER_WRITE(sc); + } + + if (sc->sc_monitor.mirror_tx) { + reg = AR40XX_REG_READ(sc, + AR40XX_REG_PORT_HOL_CTRL1(sc->sc_monitor.source_port)); + reg |= AR40XX_PORT_HOL_CTRL1_EG_MIRROR_EN; + AR40XX_REG_WRITE(sc, + AR40XX_REG_PORT_HOL_CTRL1(sc->sc_monitor.source_port), + reg); + AR40XX_REG_BARRIER_WRITE(sc); + } + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_port.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_port.h @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_PORT_H__ +#define __AR40XX_HW_PORT_H__ + +extern int ar40xx_hw_port_init(struct ar40xx_softc *sc, int port); +extern int ar40xx_hw_port_cpuport_setup(struct ar40xx_softc *sc); +extern int ar40xx_hw_port_link_up(struct ar40xx_softc *sc, int port); +extern int ar40xx_hw_port_link_down(struct ar40xx_softc *sc, int port); +extern int ar40xx_hw_get_port_pvid(struct ar40xx_softc *sc, int port, + int *pvid); +extern int ar40xx_hw_set_port_pvid(struct ar40xx_softc *sc, int port, + int pvid); +extern int ar40xx_hw_port_setup(struct ar40xx_softc *sc, int port, + uint32_t members); + +#endif /* __AR40XX_HW_PORT_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c @@ -0,0 +1,289 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +int +ar40xx_hw_port_init(struct ar40xx_softc *sc, int port) +{ + uint32_t reg; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, + "%s: called; port %d\n", __func__, port); + + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HEADER(port), 0); + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), 0); + AR40XX_REG_BARRIER_WRITE(sc); + + DELAY(20); + + /* + * Ok! Here is where things get super fun in the AR40xx + * driver in uboot/linux. + * + * The earlier chipset switch drivers enable auto link enable here. + * The switch will poll the PHYs too, and configure appropriately. + * + * The ar40xx code in linux/u-boot instead has a whole workaround + * path that polls things directly and does some weird hijinx. + * NOTABLY - they do NOT enable the TX/RX MAC here or autoneg - + * it's done in the work around path. + * + * SO - for now the port is left off until the PHY state changes. + * And then we flip it on and off based on the PHY state. + */ +#if 0 + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), + AR40XX_PORT_AUTO_LINK_EN); +#endif + + /* + * Configure the VLAN egress mode (don't touch them) and + * learning state for STP/ATU. This isn't currently + * configurable so it's just nailed up here and left alone. + */ + reg = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH + << AR40XX_PORT_VLAN1_OUT_MODE_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); + + reg = AR40XX_PORT_LOOKUP_LEARN; + reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +/* + * Call when the link for a non-CPU port is down. + * + * This will turn off the MAC/forwarding path for this port. + */ +int +ar40xx_hw_port_link_down(struct ar40xx_softc *sc, int port) +{ + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, + "%s: called; port %d\n", __func__, port); + + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); + + return (0); +} + +/* + * Call when the link for a non-CPU port is up. + * + * This will turn on the default auto-link checking and + * eventually enable the TX/RX MAC. + */ +int +ar40xx_hw_port_link_up(struct ar40xx_softc *sc, int port) +{ + uint32_t reg; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, + "%s: called; port %d\n", __func__, port); + + /* Auto-link enable */ + AR40XX_REG_BARRIER_READ(sc); + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(port)); + reg |= AR40XX_PORT_AUTO_LINK_EN; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +/* + * Setup the CPU facing port. For this device it'll only + * be port 0. + */ +int +ar40xx_hw_port_cpuport_setup(struct ar40xx_softc *sc) +{ + uint32_t reg; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called\n", + __func__); + + reg = AR40XX_PORT_STATUS_TXFLOW + | AR40XX_PORT_STATUS_RXFLOW + | AR40XX_PORT_TXHALF_FLOW + | AR40XX_PORT_DUPLEX + | AR40XX_PORT_SPEED_1000M; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); + DELAY(20); + + reg |= AR40XX_PORT_TX_EN | AR40XX_PORT_RX_EN; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +/* + * Fetch the port PVID. + * + * For 802.1q mode this is the default VLAN ID for the port. + * Frames without an 802.1q VLAN will assume this VLAN ID for + * transmit/receive. + */ +int +ar40xx_hw_get_port_pvid(struct ar40xx_softc *sc, int port, int *pvid) +{ + uint32_t reg; + + AR40XX_LOCK_ASSERT(sc); + + AR40XX_REG_BARRIER_READ(sc); + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(port)); + + reg = reg >> AR40XX_PORT_VLAN0_DEF_CVID_S; + reg = reg & 0x0fff; /* XXX */ + + *pvid = reg; + return (0); +} + +/* + * Set the port PVID. + * + * For now, since double-tagged frames aren't currently supported, + * CVID=SVID here. + */ +int +ar40xx_hw_set_port_pvid(struct ar40xx_softc *sc, int port, int pvid) +{ + uint32_t reg; + + AR40XX_LOCK_ASSERT(sc); + + pvid &= ETHERSWITCH_VID_MASK; + + reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; + reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +/* + * Setup the default port membership configuration. + * + * This configures the PVID for the port in the sc_vlan config, + * along with a set of ports that constitute the "membership" + * of this particular VID. + * + * For 802.1q mode the membership can be viewed as the default + * learning port group, but this can be added to via VLAN membership. + * (Eg you could in theory split two LAN ports into separate "member" + * groups and they'd not learn MAC addresses from each other even + * inside a VLAN; you'd then end up with the traffic being flooded to + * the CPU port.) + */ +int +ar40xx_hw_port_setup(struct ar40xx_softc *sc, int port, uint32_t members) +{ + uint32_t egress, ingress, reg; + uint32_t pvid = sc->sc_vlan.vlan_id[sc->sc_vlan.pvid[port]] + & ETHERSWITCH_VID_MASK; + + if (sc->sc_vlan.vlan) { + egress = AR40XX_PORT_VLAN1_OUT_MODE_UNMOD; + ingress = AR40XX_IN_SECURE; + } else { + egress = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH; + ingress = AR40XX_IN_PORT_ONLY; + } + + reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; + reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + reg = AR40XX_PORT_VLAN1_PORT_VLAN_PROP; + reg |= egress << AR40XX_PORT_VLAN1_OUT_MODE_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + reg = members; + reg |= AR40XX_PORT_LOOKUP_LEARN; + reg |= ingress << AR40XX_PORT_LOOKUP_IN_MODE_S; + reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_PSGMII_H__ +#define __AR40XX_HW_PSGMII_H__ + +extern int ar40xx_hw_psgmii_set_mac_mode(struct ar40xx_softc *sc, + uint32_t mac_mode); +extern int ar40xx_hw_psgmii_self_test(struct ar40xx_softc *sc); +extern int ar40xx_hw_psgmii_self_test_clean(struct ar40xx_softc *sc); +extern int ar40xx_hw_psgmii_single_phy_testing(struct ar40xx_softc *sc, + int phy); +extern int ar40xx_hw_malibu_psgmii_ess_reset(struct ar40xx_softc *sc); +extern int ar40xx_hw_psgmii_all_phy_testing(struct ar40xx_softc *sc); +extern int ar40xx_hw_psgmii_init_config(struct ar40xx_softc *sc); + +#endif /* __AR40XX_HW_PSGMII_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c @@ -0,0 +1,439 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +/* + * Routines that control the ess-psgmii block - the interconnect + * between the ess-switch and the external multi-port PHY + * (eg Maple.) + */ + +static void +ar40xx_hw_psgmii_reg_write(struct ar40xx_softc *sc, uint32_t reg, + uint32_t val) +{ + bus_space_write_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, + reg, val); + bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, + 0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_WRITE); +} + +static int +ar40xx_hw_psgmii_reg_read(struct ar40xx_softc *sc, uint32_t reg) +{ + int ret; + + bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, + 0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_READ); + ret = bus_space_read_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, + reg); + + return (ret); +} + +int +ar40xx_hw_psgmii_set_mac_mode(struct ar40xx_softc *sc, uint32_t mac_mode) +{ + if (mac_mode == PORT_WRAPPER_PSGMII) { + ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMII_MODE_CONTROL, + 0x2200); + ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMIIPHY_TX_CONTROL, + 0x8380); + } else { + device_printf(sc->sc_dev, "WARNING: unknown MAC_MODE=%u\n", + mac_mode); + } + + return (0); +} + +int +ar40xx_hw_psgmii_single_phy_testing(struct ar40xx_softc *sc, int phy) +{ + int j; + uint32_t tx_ok, tx_error; + uint32_t rx_ok, rx_error; + uint32_t tx_ok_high16; + uint32_t rx_ok_high16; + uint32_t tx_all_ok, rx_all_ok; + + MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x9000); + MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x4140); + + for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) { + uint16_t status; + + status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11); + if (status & AR40XX_PHY_SPEC_STATUS_LINK) + break; + /* + * the polling interval to check if the PHY link up + * or not + * maxwait_timer: 750 ms +/-10 ms + * minwait_timer : 1 us +/- 0.1us + * time resides in minwait_timer ~ maxwait_timer + * see IEEE 802.3 section 40.4.5.2 + */ + DELAY(8 * 1000); + } + + /* enable check */ + ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0000); + ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0003); + + /* start traffic */ + ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8020, 0xa000); + /* + *wait for all traffic end + * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms + */ + DELAY(60 * 1000); + + /* check counter */ + tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e); + tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d); + tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f); + rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b); + rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a); + rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c); + tx_all_ok = tx_ok + (tx_ok_high16 << 16); + rx_all_ok = rx_ok + (rx_ok_high16 << 16); + + if (tx_all_ok == 0x1000 && tx_error == 0) { + /* success */ + sc->sc_psgmii.phy_t_status &= ~(1U << phy); + } else { + device_printf(sc->sc_dev, "TX_OK=%d, tx_error=%d RX_OK=%d" + " rx_error=%d\n", + tx_all_ok, tx_error, rx_all_ok, rx_error); + device_printf(sc->sc_dev, + "PHY %d single test PSGMII issue happen!\n", phy); + sc->sc_psgmii.phy_t_status |= BIT(phy); + } + + MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x1840); + return (0); +} + +int +ar40xx_hw_psgmii_all_phy_testing(struct ar40xx_softc *sc) +{ + int phy, j; + + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9000); + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x4140); + + for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) { + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { + uint16_t status; + + status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11); + if (!(status & (1U << 10))) + break; + } + + if (phy >= (AR40XX_NUM_PORTS - 1)) + break; + /* The polling interval to check if the PHY link up or not */ + DELAY(8*1000); + } + + /* enable check */ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0000); + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0003); + + /* start traffic */ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0xa000); + /* + * wait for all traffic end + * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms + */ + DELAY(60*1000); /* was 50ms */ + + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { + uint32_t tx_ok, tx_error; + uint32_t rx_ok, rx_error; + uint32_t tx_ok_high16; + uint32_t rx_ok_high16; + uint32_t tx_all_ok, rx_all_ok; + + /* check counter */ + tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e); + tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d); + tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f); + rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b); + rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a); + rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c); + + tx_all_ok = tx_ok + (tx_ok_high16<<16); + rx_all_ok = rx_ok + (rx_ok_high16<<16); + if (tx_all_ok == 0x1000 && tx_error == 0) { + /* success */ + sc->sc_psgmii.phy_t_status &= ~(1U << (phy + 8)); + } else { + device_printf(sc->sc_dev, + "PHY%d test see issue! (tx_all_ok=%u," + " rx_all_ok=%u, tx_error=%u, rx_error=%u)\n", + phy, tx_all_ok, rx_all_ok, tx_error, rx_error); + sc->sc_psgmii.phy_t_status |= (1U << (phy + 8)); + } + } + + device_printf(sc->sc_dev, "PHY all test 0x%x\n", + sc->sc_psgmii.phy_t_status); + return (0); +} + +/* + * Reset PSGMII in the Malibu PHY. + */ +int +ar40xx_hw_malibu_psgmii_ess_reset(struct ar40xx_softc *sc) +{ + device_printf(sc->sc_dev, "%s: called\n", __func__); + uint32_t i; + + /* reset phy psgmii */ + /* fix phy psgmii RX 20bit */ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b); + /* reset phy psgmii */ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x001b); + /* release reset phy psgmii */ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b); + + for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { + uint32_t status; + + status = ar40xx_hw_phy_mmd_read(sc, 5, 1, 0x28); + if (status & (1U << 0)) + break; + /* + * Polling interval to check PSGMII PLL in malibu is ready + * the worst time is 8.67ms + * for 25MHz reference clock + * [512+(128+2048)*49]*80ns+100us + */ + DELAY(2000); + } + /* XXX TODO ;see if it timed out? */ + + /*check malibu psgmii calibration done end..*/ + + /*freeze phy psgmii RX CDR*/ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x2230); + + ar40xx_hw_ess_reset(sc); + + /*check psgmii calibration done start*/ + for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { + uint32_t status; + + status = ar40xx_hw_psgmii_reg_read(sc, 0xa0); + if (status & (1U << 0)) + break; + /* Polling interval to check PSGMII PLL in ESS is ready */ + DELAY(2000); + } + /* XXX TODO ;see if it timed out? */ + + /* check dakota psgmii calibration done end..*/ + + /* release phy psgmii RX CDR */ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x3230); + /* release phy psgmii RX 20bit */ + MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005f); + + return (0); +} + +int +ar40xx_hw_psgmii_self_test(struct ar40xx_softc *sc) +{ + uint32_t i, phy, reg; + + device_printf(sc->sc_dev, "%s: called\n", __func__); + + ar40xx_hw_malibu_psgmii_ess_reset(sc); + + /* switch to access MII reg for copper */ + MDIO_WRITEREG(sc->sc_mdio_dev, 4, 0x1f, 0x8500); + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { + /*enable phy mdio broadcast write*/ + ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x801f); + } + + /* force no link by power down */ + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x1840); + + /* packet number*/ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x1000); + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8062, 0x05e0); + + /* fix mdi status */ + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6800); + for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { + sc->sc_psgmii.phy_t_status = 0; + + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { + /* Enable port loopback for testing */ + AR40XX_REG_BARRIER_READ(sc); + reg = AR40XX_REG_READ(sc, + AR40XX_REG_PORT_LOOKUP(phy + 1)); + reg |= AR40XX_PORT_LOOKUP_LOOPBACK; + AR40XX_REG_WRITE(sc, + AR40XX_REG_PORT_LOOKUP(phy + 1), reg); + AR40XX_REG_BARRIER_WRITE(sc); + } + + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) + ar40xx_hw_psgmii_single_phy_testing(sc, phy); + + ar40xx_hw_psgmii_all_phy_testing(sc); + + if (sc->sc_psgmii.phy_t_status) + ar40xx_hw_malibu_psgmii_ess_reset(sc); + else + break; + } + + if (i >= AR40XX_PSGMII_CALB_NUM) + device_printf(sc->sc_dev, "PSGMII cannot recover\n"); + else + device_printf(sc->sc_dev, + "PSGMII recovered after %d times reset\n", i); + + /* configuration recover */ + /* packet number */ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x0); + /* disable check */ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0); + /* disable traffic */ + ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0x0); + + return (0); +} + +int +ar40xx_hw_psgmii_self_test_clean(struct ar40xx_softc *sc) +{ + uint32_t reg; + int phy; + + device_printf(sc->sc_dev, "%s: called\n", __func__); + + /* disable phy internal loopback */ + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6860); + MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9040); + + for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { + /* disable mac loop back */ + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(phy + 1)); + reg &= ~AR40XX_PORT_LOOKUP_LOOPBACK; + AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(phy + 1), reg); + AR40XX_REG_BARRIER_WRITE(sc); + + /* disable phy mdio broadcast write */ + ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x001f); + } + + /* clear fdb entry */ + ar40xx_hw_atu_flush_all(sc); + + return (0); +} + +int +ar40xx_hw_psgmii_init_config(struct ar40xx_softc *sc) +{ + uint32_t reg; + + /* + * This is based on what I found in uboot - it configures + * the initial ESS interconnect to either be PSGMII + * or RGMII. + */ + + /* For now, just assume PSGMII and fix it in post. */ + /* PSGMIIPHY_PLL_VCO_RELATED_CTRL */ + reg = ar40xx_hw_psgmii_reg_read(sc, 0x78c); + device_printf(sc->sc_dev, + "%s: PSGMIIPHY_PLL_VCO_RELATED_CTRL=0x%08x\n", __func__, reg); + /* PSGMIIPHY_VCO_CALIBRATION_CTRL */ + reg = ar40xx_hw_psgmii_reg_read(sc, 0x09c); + device_printf(sc->sc_dev, + "%s: PSGMIIPHY_VCO_CALIBRATION_CTRL=0x%08x\n", __func__, reg); + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.h @@ -0,0 +1,41 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_HW_VTU_H__ +#define __AR40XX_HW_VTU_H__ + +extern int ar40xx_hw_vtu_op(struct ar40xx_softc *sc, uint32_t op, + uint32_t val); +extern int ar40xx_hw_vtu_load_vlan(struct ar40xx_softc *sc, uint32_t vid, + uint32_t port_mask, uint32_t untagged_mask); +extern int ar40xx_hw_vtu_flush(struct ar40xx_softc *sc); +extern int ar40xx_hw_vtu_get_vlan(struct ar40xx_softc *sc, int vid, + uint32_t *ports, uint32_t *untagged_ports); + +#endif /* __AR40XX_HW_VTU_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c @@ -0,0 +1,198 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +/* + * Perform a VTU (vlan table unit) operation. + */ +int +ar40xx_hw_vtu_op(struct ar40xx_softc *sc, uint32_t op, uint32_t val) +{ + int ret; + + AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, + "%s: called; op=0x%08x, val=0x%08x\n", + __func__, op, val); + + ret = (ar40xx_hw_wait_bit(sc, AR40XX_REG_VTU_FUNC1, + AR40XX_VTU_FUNC1_BUSY, 0)); + if (ret != 0) + return (ret); + + if ((op & AR40XX_VTU_FUNC1_OP) == AR40XX_VTU_FUNC1_OP_LOAD) { + AR40XX_REG_WRITE(sc, AR40XX_REG_VTU_FUNC0, val); + AR40XX_REG_BARRIER_WRITE(sc); + } + + op |= AR40XX_VTU_FUNC1_BUSY; + AR40XX_REG_WRITE(sc, AR40XX_REG_VTU_FUNC1, op); + AR40XX_REG_BARRIER_WRITE(sc); + + return (0); +} + +/* + * Load in a VLAN table map / port configuration for the given + * vlan ID. + */ +int +ar40xx_hw_vtu_load_vlan(struct ar40xx_softc *sc, uint32_t vid, + uint32_t port_mask, uint32_t untagged_mask) +{ + + uint32_t op, val, mode; + int i, ret; + + AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, + "%s: called; vid=%d port_mask=0x%08x, untagged_mask=0x%08x\n", + __func__, vid, port_mask, untagged_mask); + + op = AR40XX_VTU_FUNC1_OP_LOAD | (vid << AR40XX_VTU_FUNC1_VID_S); + val = AR40XX_VTU_FUNC0_VALID | AR40XX_VTU_FUNC0_IVL; + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + if ((port_mask & (1U << i)) == 0) + /* Not in the VLAN at all */ + mode = AR40XX_VTU_FUNC0_EG_MODE_NOT; + else if (sc->sc_vlan.vlan == 0) + /* VLAN mode disabled; keep the provided VLAN tag */ + mode = AR40XX_VTU_FUNC0_EG_MODE_KEEP; + else if (untagged_mask & (1U << i)) + /* Port in the VLAN; is untagged */ + mode = AR40XX_VTU_FUNC0_EG_MODE_UNTAG; + else + /* Port is in the VLAN; is tagged */ + mode = AR40XX_VTU_FUNC0_EG_MODE_TAG; + val |= mode << AR40XX_VTU_FUNC0_EG_MODE_S(i); + } + ret = ar40xx_hw_vtu_op(sc, op, val); + + return (ret); +} + +/* + * Flush all VLAN port entries. + */ +int +ar40xx_hw_vtu_flush(struct ar40xx_softc *sc) +{ + int ret; + + AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, "%s: called\n", __func__); + + ret = ar40xx_hw_vtu_op(sc, AR40XX_VTU_FUNC1_OP_FLUSH, 0); + return (ret); +} + +/* + * Get the VLAN port map for the given vlan ID. + */ +int +ar40xx_hw_vtu_get_vlan(struct ar40xx_softc *sc, int vid, uint32_t *ports, + uint32_t *untagged_ports) +{ + uint32_t op, reg, val; + int i, r; + + op = AR40XX_VTU_FUNC1_OP_GET_ONE; + + /* Filter out any etherswitch VID flags; only grab the VLAN ID */ + vid &= ETHERSWITCH_VID_MASK; + + /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */ + op |= (vid << AR40XX_VTU_FUNC1_VID_S); + r = ar40xx_hw_vtu_op(sc, op, 0); + if (r != 0) { + device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid); + return (r); + } + + AR40XX_REG_BARRIER_READ(sc); + reg = AR40XX_REG_READ(sc, AR40XX_REG_VTU_FUNC0); + + *ports = 0; + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + val = reg >> AR40XX_VTU_FUNC0_EG_MODE_S(i); + val = val & 0x3; + /* XXX KEEP (unmodified? For non-dot1q operation?) */ + if (val == AR40XX_VTU_FUNC0_EG_MODE_TAG) { + *ports |= (1 << i); + } else if (val == AR40XX_VTU_FUNC0_EG_MODE_UNTAG) { + *ports |= (1 << i); + *untagged_ports |= (1 << i); + } + } + + return (0); +} + Index: sys/dev/etherswitch/ar40xx/ar40xx_main.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_main.c @@ -0,0 +1,970 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + +static struct ofw_compat_data compat_data[] = { + { "qcom,ess-switch", 1 }, + { NULL, 0 }, +}; + +static int +ar40xx_probe(device_t dev) +{ + + if (! ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "IPQ4018 ESS Switch fabric / PSGMII PHY"); + return (BUS_PROBE_DEFAULT); +} + +static void +ar40xx_tick(void *arg) +{ + struct ar40xx_softc *sc = arg; + + (void) ar40xx_phy_tick(sc); + callout_reset(&sc->sc_phy_callout, hz, ar40xx_tick, sc); +} + +static void +ar40xx_statchg(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s\n", __func__); +} + +static int +ar40xx_readphy(device_t dev, int phy, int reg) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + return MDIO_READREG(sc->sc_mdio_dev, phy, reg); +} + +static int +ar40xx_writephy(device_t dev, int phy, int reg, int val) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + return MDIO_WRITEREG(sc->sc_mdio_dev, phy, reg, val); +} + +/* + * Do the initial switch configuration. + */ +static int +ar40xx_reset_switch(struct ar40xx_softc *sc) +{ + int ret, i; + + AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); + + /* blank the VLAN config */ + memset(&sc->sc_vlan, 0, sizeof(sc->sc_vlan)); + + /* initial vlan port mapping */ + for (i = 0; i < AR40XX_NUM_VTU_ENTRIES; i++) + sc->sc_vlan.vlan_id[i] = 0; + + /* init vlan config */ + ret = ar40xx_hw_vlan_init(sc); + + /* init monitor config */ + sc->sc_monitor.mirror_tx = false; + sc->sc_monitor.mirror_rx = false; + sc->sc_monitor.source_port = 0; + sc->sc_monitor.monitor_port = 0; + + /* apply switch config */ + ret = ar40xx_hw_sw_hw_apply(sc); + + return (ret); +} + +static int +ar40xx_sysctl_dump_port_state(SYSCTL_HANDLER_ARGS) +{ + struct ar40xx_softc *sc = arg1; + int val = 0; + int error; + int i; + + (void) i; (void) sc; + + error = sysctl_handle_int(oidp, &val, 0, req); + if (error || !req->newptr) + return (error); + + if (val < 0 || val > 5) { + return (EINVAL); + } + + AR40XX_LOCK(sc); + + device_printf(sc->sc_dev, "port %d: PORT_STATUS=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(val))); + device_printf(sc->sc_dev, "port %d: PORT_HEADER=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_HEADER(val))); + device_printf(sc->sc_dev, "port %d: PORT_VLAN0=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(val))); + device_printf(sc->sc_dev, "port %d: PORT_VLAN1=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN1(val))); + device_printf(sc->sc_dev, "port %d: PORT_LOOKUP=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(val))); + device_printf(sc->sc_dev, "port %d: PORT_HOL_CTRL1=0x%08x\n", val, + AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(val))); + device_printf(sc->sc_dev, "port %d: PORT_FLOWCTRL_THRESH=0x%08x\n", + val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(val))); + + AR40XX_UNLOCK(sc); + + return (0); +} + +static int +ar40xx_sysctl_dump_port_mibstats(SYSCTL_HANDLER_ARGS) +{ + struct ar40xx_softc *sc = arg1; + int val = 0; + int error; + int i; + + (void) i; (void) sc; + + error = sysctl_handle_int(oidp, &val, 0, req); + if (error || !req->newptr) + return (error); + + if (val < 0 || val > 5) { + return (EINVAL); + } + + AR40XX_LOCK(sc); + + /* Yes, this snapshots all ports */ + (void) ar40xx_hw_mib_capture(sc); + (void) ar40xx_hw_mib_fetch(sc, val); + + AR40XX_UNLOCK(sc); + + return (0); +} + + +static int +ar40xx_sysctl_attach(struct ar40xx_softc *sc) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, + "debugging flags"); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "port_state", CTLTYPE_INT | CTLFLAG_RW, sc, + 0, ar40xx_sysctl_dump_port_state, "I", ""); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "port_mibstats", CTLTYPE_INT | CTLFLAG_RW, sc, + 0, ar40xx_sysctl_dump_port_mibstats, "I", ""); + + return (0); +} + +static int +ar40xx_detach(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int i; + + device_printf(sc->sc_dev, "%s: called\n", __func__); + + callout_drain(&sc->sc_phy_callout); + + /* Free PHYs */ + for (i = 0; i < AR40XX_NUM_PHYS; i++) { + if (sc->sc_phys.miibus[i] != NULL) + device_delete_child(dev, sc->sc_phys.miibus[i]); + if (sc->sc_phys.ifp[i] != NULL) + if_free(sc->sc_phys.ifp[i]); + free(sc->sc_phys.ifname[i], M_DEVBUF); + } + + bus_generic_detach(dev); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static int +ar40xx_attach(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + phandle_t psgmii_p, root_p, mdio_p; + int ret, i; + + sc->sc_dev = dev; + mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF); + + psgmii_p = OF_finddevice("/soc/ess-psgmii"); + if (psgmii_p == -1) { + device_printf(dev, + "%s: couldn't find /soc/ess-psgmii DT node\n", + __func__); + goto error; + } + + /* + * Get the ipq4019-mdio node here, to talk to our local PHYs + * if needed + */ + root_p = OF_finddevice("/soc"); + mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio"); + if (mdio_p == -1) { + device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n", + __func__); + goto error; + } + sc->sc_mdio_phandle = mdio_p; + sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p)); + if (sc->sc_mdio_dev == NULL) { + device_printf(dev, + "%s: couldn't get mdio device (mdio_p=%u)\n", + __func__, mdio_p); + goto error; + } + + /* get psgmii base address from psgmii node */ + ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag, + &sc->sc_psgmii_mem_handle, + &sc->sc_psgmii_mem_size); + if (ret != 0) { + device_printf(dev, "%s: couldn't map psgmii mem (%d)\n", + __func__, ret); + goto error; + } + + /* get switch base address */ + sc->sc_ess_mem_rid = 0; + sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_ess_mem_rid, RF_ACTIVE); + if (sc->sc_ess_mem_res == NULL) { + device_printf(dev, "%s: failed to find memory resource\n", + __func__); + goto error; + } + sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev, + SYS_RES_MEMORY, sc->sc_ess_mem_rid); + if (sc->sc_ess_mem_size == 0) { + device_printf(dev, "%s: failed to get device memory size\n", + __func__); + goto error; + } + + ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode", + &sc->sc_config.switch_mac_mode, + sizeof(sc->sc_config.switch_mac_mode)); + if (ret < 0) { + device_printf(dev, "%s: missing switch_mac_mode property\n", + __func__); + goto error; + } + + ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp", + &sc->sc_config.switch_cpu_bmp, + sizeof(sc->sc_config.switch_cpu_bmp)); + if (ret < 0) { + device_printf(dev, "%s: missing switch_cpu_bmp property\n", + __func__); + goto error; + } + + ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp", + &sc->sc_config.switch_lan_bmp, + sizeof(sc->sc_config.switch_lan_bmp)); + if (ret < 0) { + device_printf(dev, "%s: missing switch_lan_bmp property\n", + __func__); + goto error; + } + + ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp", + &sc->sc_config.switch_wan_bmp, + sizeof(sc->sc_config.switch_wan_bmp)); + if (ret < 0) { + device_printf(dev, "%s: missing switch_wan_bmp property\n", + __func__); + goto error; + } + + ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk); + if (ret != 0) { + device_printf(dev, "%s: failed to find ess_clk (%d)\n", + __func__, ret); + goto error; + } + ret = clk_enable(sc->sc_ess_clk); + if (ret != 0) { + device_printf(dev, "%s: failed to enable clock (%d)\n", + __func__, ret); + goto error; + } + + ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst); + if (ret != 0) { + device_printf(dev, "%s: failed to find ess_rst (%d)\n", + __func__, ret); + goto error; + } + + /* + * Ok, at this point we have enough resources to do an initial + * reset and configuration. + */ + + AR40XX_LOCK(sc); + + /* Initial PSGMII/RGMII port configuration */ + ret = ar40xx_hw_psgmii_init_config(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to init PSGMII (%d)\n", ret); + goto error_locked; + } + + /* + * ESS reset - this resets both the ethernet switch + * AND the ethernet block. + */ + ret = ar40xx_hw_ess_reset(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to reset ESS block (%d)\n", ret); + goto error_locked; + } + + /* + * Check the PHY IDs for each of the PHYs from 0..4; + * this is useful to make sure that we can SEE the external + * PHY(s). + */ + if (bootverbose) { + ret = ar40xx_hw_phy_get_ids(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to check PHY IDs (%d)\n", ret); + goto error_locked; + } + } + + /* + * Do PSGMII PHY self-test; work-around issues. + */ + ret = ar40xx_hw_psgmii_self_test(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to do PSGMII self-test (%d)\n", ret); + goto error_locked; + } + + /* Return port config to runtime state */ + ret = ar40xx_hw_psgmii_self_test_clean(sc); + if (ret != 0) { + device_printf(sc->sc_dev, + "ERROR: failed to do PSGMII runtime config (%d)\n", ret); + goto error_locked; + } + + /* mac_mode_init */ + ret = ar40xx_hw_psgmii_set_mac_mode(sc, + sc->sc_config.switch_mac_mode); + + /* Initialise each hardware port */ + for (i = 0; i < AR40XX_NUM_PORTS; i++) { + ret = ar40xx_hw_port_init(sc, i); + } + + /* initialise the global switch configuration */ + ret = ar40xx_hw_init_globals(sc); + + /* reset the switch vlan/port learning config */ + ret = ar40xx_reset_switch(sc); + + /* cpuport setup */ + ret = ar40xx_hw_port_cpuport_setup(sc); + + AR40XX_UNLOCK(sc); + +#if 0 + /* We may end up needing the QM workaround code here.. */ + device_printf(dev, "%s: TODO: QM error check\n", __func__); +#endif + + /* Attach PHYs */ + ret = ar40xx_attach_phys(sc); + + ret = bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + ret = bus_generic_attach(dev); + + /* Start timer */ + callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0); + + /* + * Setup the etherswitch info block. + */ + strlcpy(sc->sc_info.es_name, device_get_desc(dev), + sizeof(sc->sc_info.es_name)); + sc->sc_info.es_nports = AR40XX_NUM_PORTS; + sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q; + /* XXX TODO: double-tag / 802.1ad */ + sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES; + + /* + * Fetch the initial port configuration. + */ + AR40XX_LOCK(sc); + ar40xx_tick(sc); + AR40XX_UNLOCK(sc); + + ar40xx_sysctl_attach(sc); + + return (0); +error_locked: + AR40XX_UNLOCK(sc); +error: + ar40xx_detach(dev); + return (ENXIO); +} + +static void +ar40xx_lock(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + AR40XX_LOCK(sc); +} + +static void +ar40xx_unlock(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + AR40XX_LOCK_ASSERT(sc); + AR40XX_UNLOCK(sc); +} + +static etherswitch_info_t * +ar40xx_getinfo(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + return (&sc->sc_info); +} + +static int +ar40xx_readreg(device_t dev, int addr) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + if (addr >= sc->sc_ess_mem_size - 1) + return (-1); + + AR40XX_REG_BARRIER_READ(sc); + + return AR40XX_REG_READ(sc, addr); +} + +static int +ar40xx_writereg(device_t dev, int addr, int value) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + + if (addr >= sc->sc_ess_mem_size - 1) + return (-1); + + AR40XX_REG_WRITE(sc, addr, value); + AR40XX_REG_BARRIER_WRITE(sc); + return (0); +} + +/* + * Get the port configuration and status. + */ +static int +ar40xx_getport(device_t dev, etherswitch_port_t *p) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + struct mii_data *mii = NULL; + struct ifmediareq *ifmr; + int err; + + if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) + return (ENXIO); + + AR40XX_LOCK(sc); + /* Fetch the current VLAN configuration for this port */ + /* PVID */ + ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid); + + /* + * The VLAN egress aren't appropriate to the ports; + * instead it's part of the VLAN group config. + */ + + /* Get MII config */ + mii = ar40xx_phy_miiforport(sc, p->es_port); + + AR40XX_UNLOCK(sc); + + if (p->es_port == 0) { + /* CPU port */ + p->es_flags |= ETHERSWITCH_PORT_CPU; + ifmr = &p->es_ifmr; + ifmr->ifm_count = 0; + ifmr->ifm_current = ifmr->ifm_active = + IFM_ETHER | IFM_1000_T | IFM_FDX; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; + } else if (mii != NULL) { + /* non-CPU port */ + err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, + &mii->mii_media, SIOCGIFMEDIA); + if (err) + return (err); + } else { + return (ENXIO); + } + + return (0); +} + +/* + * Set the port configuration and status. + */ +static int +ar40xx_setport(device_t dev, etherswitch_port_t *p) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + struct ifmedia *ifm; + struct mii_data *mii; + struct ifnet *ifp; + int ret; + + if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) + return (EINVAL); + + /* Port flags */ + AR40XX_LOCK(sc); + ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid); + if (ret != 0) { + AR40XX_UNLOCK(sc); + return (ret); + } + /* XXX TODO: tag strip/unstrip, double-tag, etc */ + AR40XX_UNLOCK(sc); + + /* Don't change media config on CPU port */ + if (p->es_port == 0) + return (0); + + mii = ar40xx_phy_miiforport(sc, p->es_port); + if (mii == NULL) + return (ENXIO); + + ifp = ar40xx_phy_ifpforport(sc, p->es_port); + + ifm = &mii->mii_media; + return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); + + return (0); +} + +/* + * Get the current VLAN group (per-port, ISL, dot1q) configuration. + * + * For now the only supported operating mode is dot1q. + */ +static int +ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int vid, ret; + + if (vg->es_vlangroup > sc->sc_info.es_nvlangroups) + return (EINVAL); + + vg->es_untagged_ports = 0; + vg->es_member_ports = 0; + vg->es_fid = 0; + + AR40XX_LOCK(sc); + + /* Note: only supporting 802.1q VLAN config for now */ + if (sc->sc_vlan.vlan != 1) { + vg->es_member_ports = 0; + vg->es_untagged_ports = 0; + AR40XX_UNLOCK(sc); + return (-1); + } + + /* Get vlangroup mapping to VLAN id */ + vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; + if ((vid & ETHERSWITCH_VID_VALID) == 0) { + /* Not an active vgroup; bail */ + AR40XX_UNLOCK(sc); + return (0); + } + vg->es_vid = vid; + + ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports, + &vg->es_untagged_ports); + + AR40XX_UNLOCK(sc); + + if (ret == 0) { + vg->es_vid |= ETHERSWITCH_VID_VALID; + } + + return (ret); +} + +/* + * Set the current VLAN group (per-port, ISL, dot1q) configuration. + * + * For now the only supported operating mode is dot1q. + */ +static int +ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int err, vid; + + /* For now we only support 802.1q mode */ + if (sc->sc_vlan.vlan == 0) + return (EINVAL); + + AR40XX_LOCK(sc); + vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; + /* + * If we have an 802.1q VID and it's different to the current one, + * purge the current VTU entry. + */ + if ((vid != 0) && + ((vid & ETHERSWITCH_VID_VALID) != 0) && + ((vid & ETHERSWITCH_VID_MASK) != + (vg->es_vid & ETHERSWITCH_VID_MASK))) { + AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, + "%s: purging VID %d first\n", __func__, vid); + err = ar40xx_hw_vtu_flush(sc); + if (err != 0) { + AR40XX_UNLOCK(sc); + return (err); + } + } + + /* Update VLAN ID */ + vid = vg->es_vid & ETHERSWITCH_VID_MASK; + sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid; + if (vid == 0) { + /* Setting it to 0 disables the group */ + AR40XX_UNLOCK(sc); + return (0); + } + /* Add valid bit for this entry */ + sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID; + + /* Update hardware */ + err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports, + vg->es_untagged_ports); + if (err != 0) { + AR40XX_UNLOCK(sc); + return (err); + } + + /* Update the config for the given entry */ + sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports; + sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports; + + AR40XX_UNLOCK(sc); + + return (0); +} + +/* + * Get the current configuration mode. + */ +static int +ar40xx_getconf(device_t dev, etherswitch_conf_t *conf) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int ret; + + AR40XX_LOCK(sc); + + /* Only support dot1q VLAN for now */ + conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; + conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; + + /* Switch MAC address */ + ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr); + if (ret == 0) + conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; + + AR40XX_UNLOCK(sc); + + return (0); +} + +/* + * Set the current configuration and do a switch reset. + * + * For now the only supported operating mode is dot1q, don't + * allow it to be set to non-dot1q. + */ +static int +ar40xx_setconf(device_t dev, etherswitch_conf_t *conf) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int ret = 0; + + if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { + /* Only support dot1q VLAN for now */ + if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) + return (EINVAL); + } + + if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) { + AR40XX_LOCK(sc); + ret = ar40xx_hw_read_switch_mac_address(sc, + &conf->switch_macaddr); + AR40XX_UNLOCK(sc); + } + + return (ret); +} + +/* + * Flush all ATU entries. + */ +static int +ar40xx_atu_flush_all(device_t dev) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int ret; + + AR40XX_LOCK(sc); + ret = ar40xx_hw_atu_flush_all(sc); + AR40XX_UNLOCK(sc); + return (ret); +} + +/* + * Flush all ATU entries for the given port. + */ +static int +ar40xx_atu_flush_port(device_t dev, int port) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int ret; + + AR40XX_LOCK(sc); + ret = ar40xx_hw_atu_flush_port(sc, port); + AR40XX_UNLOCK(sc); + return (ret); +} + +/* + * Load the ATU table into local storage so it can be iterated + * over. + */ +static int +ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int err, nitems; + + memset(&sc->atu.entries, 0, sizeof(sc->atu.entries)); + + table->es_nitems = 0; + nitems = 0; + + AR40XX_LOCK(sc); + sc->atu.count = 0; + err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0); + if (err != 0) + goto done; + + while (nitems < AR40XX_NUM_ATU_ENTRIES) { + err = ar40xx_hw_atu_fetch_entry(sc, + &sc->atu.entries[nitems], 1); + if (err != 0) + goto done; + sc->atu.entries[nitems].id = nitems; + nitems++; + } +done: + sc->atu.count = nitems; + table->es_nitems = nitems; + AR40XX_UNLOCK(sc); + + return (0); +} + +/* + * Iterate over the ATU table entries that have been previously + * fetched. + */ +static int +ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) +{ + struct ar40xx_softc *sc = device_get_softc(dev); + int id, err = 0; + + id = e->id; + AR40XX_LOCK(sc); + if (id > sc->atu.count) { + err = ENOENT; + goto done; + } + memcpy(e, &sc->atu.entries[id], sizeof(*e)); +done: + AR40XX_UNLOCK(sc); + return (err); +} + +static device_method_t ar40xx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ar40xx_probe), + DEVMETHOD(device_attach, ar40xx_attach), + DEVMETHOD(device_detach, ar40xx_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MII interface */ + DEVMETHOD(miibus_readreg, ar40xx_readphy), + DEVMETHOD(miibus_writereg, ar40xx_writephy), + DEVMETHOD(miibus_statchg, ar40xx_statchg), + + /* MDIO interface */ + DEVMETHOD(mdio_readreg, ar40xx_readphy), + DEVMETHOD(mdio_writereg, ar40xx_writephy), + + /* etherswitch interface */ + DEVMETHOD(etherswitch_lock, ar40xx_lock), + DEVMETHOD(etherswitch_unlock, ar40xx_unlock), + DEVMETHOD(etherswitch_getinfo, ar40xx_getinfo), + DEVMETHOD(etherswitch_readreg, ar40xx_readreg), + DEVMETHOD(etherswitch_writereg, ar40xx_writereg), + DEVMETHOD(etherswitch_readphyreg, ar40xx_readphy), + DEVMETHOD(etherswitch_writephyreg, ar40xx_writephy), + DEVMETHOD(etherswitch_getport, ar40xx_getport), + DEVMETHOD(etherswitch_setport, ar40xx_setport), + DEVMETHOD(etherswitch_getvgroup, ar40xx_getvgroup), + DEVMETHOD(etherswitch_setvgroup, ar40xx_setvgroup), + DEVMETHOD(etherswitch_getconf, ar40xx_getconf), + DEVMETHOD(etherswitch_setconf, ar40xx_setconf), + DEVMETHOD(etherswitch_flush_all, ar40xx_atu_flush_all), + DEVMETHOD(etherswitch_flush_port, ar40xx_atu_flush_port), + DEVMETHOD(etherswitch_fetch_table, ar40xx_atu_fetch_table), + DEVMETHOD(etherswitch_fetch_table_entry, + ar40xx_atu_fetch_table_entry), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods, + sizeof(struct ar40xx_softc)); +static devclass_t ar40xx_devclass; + +DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, ar40xx_devclass, 0, 0); +DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, ar40xx_devclass, 0, 0); +DRIVER_MODULE(miibus, ar40xx, miibus_driver, miibus_devclass, 0, 0); +DRIVER_MODULE(mdio, ar40xx, mdio_driver, mdio_devclass, 0, 0); +DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, etherswitch_devclass, 0, 0); +MODULE_DEPEND(ar40xx, mdio, 1, 1, 1); +MODULE_DEPEND(ar40xx, miibus, 1, 1, 1); +MODULE_DEPEND(ar40xx, etherswitch, 1, 1, 1); +MODULE_VERSION(ar40xx, 1); Index: sys/dev/etherswitch/ar40xx/ar40xx_phy.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_phy.h @@ -0,0 +1,41 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_PHY_H__ +#define __AR40XX_PHY_H__ + +extern int ar40xx_phy_tick(struct ar40xx_softc *sc); +extern int ar40xx_attach_phys(struct ar40xx_softc *sc); +extern int ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc); +extern struct mii_data * ar40xx_phy_miiforport(struct ar40xx_softc *sc, + int port); +extern struct ifnet * ar40xx_phy_ifpforport(struct ar40xx_softc *sc, + int port); + +#endif /* __AR40XX_PHY_H__ */ + Index: sys/dev/etherswitch/ar40xx/ar40xx_phy.c =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_phy.c @@ -0,0 +1,254 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +int +ar40xx_phy_tick(struct ar40xx_softc *sc) +{ + struct mii_softc *miisc; + struct mii_data *mii; + int phy; + uint32_t reg; + + AR40XX_LOCK_ASSERT(sc); + + AR40XX_REG_BARRIER_READ(sc); + /* + * Loop over; update phy port status here + */ + for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { + /* + * Port here is PHY, not port! + */ + reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(phy + 1)); + + mii = device_get_softc(sc->sc_phys.miibus[phy]); + + /* + * Compare the current link status to the previous link + * status. We may need to clear ATU / change phy config. + */ + if (((reg & AR40XX_PORT_STATUS_LINK_UP) != 0) && + (mii->mii_media_status & IFM_ACTIVE) == 0) { + AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, + "%s: PHY %d: down -> up\n", __func__, phy); + ar40xx_hw_port_link_up(sc, phy + 1); + ar40xx_hw_atu_flush_port(sc, phy + 1); + } + if (((reg & AR40XX_PORT_STATUS_LINK_UP) == 0) && + (mii->mii_media_status & IFM_ACTIVE) != 0) { + AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, + "%s: PHY %d: up -> down\n", __func__, phy); + ar40xx_hw_port_link_down(sc, phy + 1); + ar40xx_hw_atu_flush_port(sc, phy + 1); + } + + mii_tick(mii); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != + miisc->mii_inst) + continue; + ukphy_status(miisc); + mii_phy_update(miisc, MII_POLLSTAT); + } + } + + return (0); +} + +static inline int +ar40xx_portforphy(int phy) +{ + + return (phy+1); +} + +struct mii_data * +ar40xx_phy_miiforport(struct ar40xx_softc *sc, int port) +{ + int phy; + + phy = port-1; + + if (phy < 0 || phy >= AR40XX_NUM_PHYS) + return (NULL); + return (device_get_softc(sc->sc_phys.miibus[phy])); +} + +struct ifnet * +ar40xx_phy_ifpforport(struct ar40xx_softc *sc, int port) +{ + int phy; + + phy = port-1; + if (phy < 0 || phy >= AR40XX_NUM_PHYS) + return (NULL); + return (sc->sc_phys.ifp[phy]); +} + +static int +ar40xx_ifmedia_upd(struct ifnet *ifp) +{ + struct ar40xx_softc *sc = ifp->if_softc; + struct mii_data *mii = ar40xx_phy_miiforport(sc, ifp->if_dunit); + + AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", + __func__, ifp->if_dunit); + + if (mii == NULL) + return (ENXIO); + mii_mediachg(mii); + return (0); +} + +static void +ar40xx_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct ar40xx_softc *sc = ifp->if_softc; + struct mii_data *mii = ar40xx_phy_miiforport(sc, ifp->if_dunit); + + AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", + __func__, ifp->if_dunit); + + if (mii == NULL) + return; + mii_pollstat(mii); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +int +ar40xx_attach_phys(struct ar40xx_softc *sc) +{ + int phy, err = 0; + char name[IFNAMSIZ]; + + /* PHYs need an interface, so we generate a dummy one */ + snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); + for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { + sc->sc_phys.ifp[phy] = if_alloc(IFT_ETHER); + if (sc->sc_phys.ifp[phy] == NULL) { + device_printf(sc->sc_dev, + "PHY %d: couldn't allocate ifnet structure\n", + phy); + err = ENOMEM; + break; + } + + sc->sc_phys.ifp[phy]->if_softc = sc; + sc->sc_phys.ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | + IFF_DRV_RUNNING | IFF_SIMPLEX; + sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, + M_WAITOK); + bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1); + if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy], + ar40xx_portforphy(phy)); + err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy], + sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd, + ar40xx_ifmedia_sts, BMSR_DEFCAPMASK, + phy, MII_OFFSET_ANY, 0); + device_printf(sc->sc_dev, + "%s attached to pseudo interface %s\n", + device_get_nameunit(sc->sc_phys.miibus[phy]), + sc->sc_phys.ifp[phy]->if_xname); + if (err != 0) { + device_printf(sc->sc_dev, + "attaching PHY %d failed\n", + phy); + return (err); + } + } + return (0); +} + +int +ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc) +{ + int phy; + uint32_t id1, id2; + + for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { + id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2); + id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3); + device_printf(sc->sc_dev, + "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n", + __func__, phy, id1, id2); + } + + return (0); +} Index: sys/dev/etherswitch/ar40xx/ar40xx_reg.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_reg.h @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __AR40XX_REG_H__ +#define __AR40XX_REG_H__ + +/* + * Register manipulation macros that expect bit field defines + * to follow the convention that an _S suffix is appended for + * a shift count, while the field mask has no suffix. + */ +#define SM(_v, _f) (((_v) << _f##_S) & (_f)) +#define MS(_v, _f) (((_v) & (_f)) >> _f##_S) + +#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) +#define BIT(_n) (1UL << (_n)) + +#define AR40XX_PORT_LINK_UP 1 +#define AR40XX_PORT_LINK_DOWN 0 +#define AR40XX_QM_NOT_EMPTY 1 +#define AR40XX_QM_EMPTY 0 + +#define AR40XX_LAN_VLAN 1 +#define AR40XX_WAN_VLAN 2 + +enum ar40xx_port_wrapper_cfg { + PORT_WRAPPER_PSGMII = 0, +}; + +struct ar40xx_mib_desc { + uint32_t size; + uint32_t offset; + const char *name; +}; + +#define AR40XX_PORT_CPU 0 + +#define AR40XX_PSGMII_MODE_CONTROL 0x1b4 +#define AR40XX_PSGMII_ATHR_CSCO_MODE_25M BIT(0) + +#define AR40XX_PSGMIIPHY_TX_CONTROL 0x288 + +#define AR40XX_MII_ATH_MMD_ADDR 0x0d +#define AR40XX_MII_ATH_MMD_DATA 0x0e +#define AR40XX_MII_ATH_DBG_ADDR 0x1d +#define AR40XX_MII_ATH_DBG_DATA 0x1e + +#define AR40XX_STATS_RXBROAD 0x00 +#define AR40XX_STATS_RXPAUSE 0x04 +#define AR40XX_STATS_RXMULTI 0x08 +#define AR40XX_STATS_RXFCSERR 0x0c +#define AR40XX_STATS_RXALIGNERR 0x10 +#define AR40XX_STATS_RXRUNT 0x14 +#define AR40XX_STATS_RXFRAGMENT 0x18 +#define AR40XX_STATS_RX64BYTE 0x1c +#define AR40XX_STATS_RX128BYTE 0x20 +#define AR40XX_STATS_RX256BYTE 0x24 +#define AR40XX_STATS_RX512BYTE 0x28 +#define AR40XX_STATS_RX1024BYTE 0x2c +#define AR40XX_STATS_RX1518BYTE 0x30 +#define AR40XX_STATS_RXMAXBYTE 0x34 +#define AR40XX_STATS_RXTOOLONG 0x38 +#define AR40XX_STATS_RXGOODBYTE 0x3c +#define AR40XX_STATS_RXBADBYTE 0x44 +#define AR40XX_STATS_RXOVERFLOW 0x4c +#define AR40XX_STATS_FILTERED 0x50 +#define AR40XX_STATS_TXBROAD 0x54 +#define AR40XX_STATS_TXPAUSE 0x58 +#define AR40XX_STATS_TXMULTI 0x5c +#define AR40XX_STATS_TXUNDERRUN 0x60 +#define AR40XX_STATS_TX64BYTE 0x64 +#define AR40XX_STATS_TX128BYTE 0x68 +#define AR40XX_STATS_TX256BYTE 0x6c +#define AR40XX_STATS_TX512BYTE 0x70 +#define AR40XX_STATS_TX1024BYTE 0x74 +#define AR40XX_STATS_TX1518BYTE 0x78 +#define AR40XX_STATS_TXMAXBYTE 0x7c +#define AR40XX_STATS_TXOVERSIZE 0x80 +#define AR40XX_STATS_TXBYTE 0x84 +#define AR40XX_STATS_TXCOLLISION 0x8c +#define AR40XX_STATS_TXABORTCOL 0x90 +#define AR40XX_STATS_TXMULTICOL 0x94 +#define AR40XX_STATS_TXSINGLECOL 0x98 +#define AR40XX_STATS_TXEXCDEFER 0x9c +#define AR40XX_STATS_TXDEFER 0xa0 +#define AR40XX_STATS_TXLATECOL 0xa4 + +#define AR40XX_REG_MODULE_EN 0x030 +#define AR40XX_MODULE_EN_MIB BIT(0) + +#define AR40XX_REG_MIB_FUNC 0x034 +#define AR40XX_MIB_BUSY BIT(17) +#define AR40XX_MIB_CPU_KEEP BIT(20) +#define AR40XX_MIB_FUNC BITS(24, 3) +#define AR40XX_MIB_FUNC_S 24 +#define AR40XX_MIB_FUNC_NO_OP 0x0 +#define AR40XX_MIB_FUNC_FLUSH 0x1 + +#define AR40XX_ESS_SERVICE_TAG 0x48 +#define AR40XX_ESS_SERVICE_TAG_STAG BIT(17) + +#define AR40XX_REG_SW_MAC_ADDR0 0x60 +#define AR40XX_REG_SW_MAC_ADDR0_BYTE4 BITS(8, 8) +#define AR40XX_REG_SW_MAC_ADDR0_BYTE4_S 8 +#define AR40XX_REG_SW_MAC_ADDR0_BYTE5 BITS(0, 8) +#define AR40XX_REG_SW_MAC_ADDR0_BYTE5_S 0 + +#define AR40XX_REG_SW_MAC_ADDR1 0x64 +#define AR40XX_REG_SW_MAC_ADDR1_BYTE0 BITS(24, 8) +#define AR40XX_REG_SW_MAC_ADDR1_BYTE0_S 24 +#define AR40XX_REG_SW_MAC_ADDR1_BYTE1 BITS(16, 8) +#define AR40XX_REG_SW_MAC_ADDR1_BYTE1_S 16 +#define AR40XX_REG_SW_MAC_ADDR1_BYTE2 BITS(8, 8) +#define AR40XX_REG_SW_MAC_ADDR1_BYTE2_S 8 +#define AR40XX_REG_SW_MAC_ADDR1_BYTE3 BITS(0, 8) +#define AR40XX_REG_SW_MAC_ADDR1_BYTE3_S 0 + +#define AR40XX_REG_MAX_FRAME_SIZE 0x078 +#define AR40XX_MAX_FRAME_SIZE_MTU BITS(0, 14) + +#define AR40XX_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) +#define AR40XX_PORT_SPEED BITS(0, 2) +#define AR40XX_PORT_STATUS_SPEED_S 0 +#define AR40XX_PORT_TX_EN BIT(2) +#define AR40XX_PORT_RX_EN BIT(3) +#define AR40XX_PORT_STATUS_TXFLOW BIT(4) +#define AR40XX_PORT_STATUS_RXFLOW BIT(5) +#define AR40XX_PORT_DUPLEX BIT(6) +#define AR40XX_PORT_TXHALF_FLOW BIT(7) +#define AR40XX_PORT_STATUS_LINK_UP BIT(8) +#define AR40XX_PORT_AUTO_LINK_EN BIT(9) +#define AR40XX_PORT_STATUS_FLOW_CONTROL BIT(12) + +#define AR40XX_REG_PORT_HEADER(_i) (0x09c + (_i) * 4) + +#define AR40XX_REG_EEE_CTRL 0x100 +#define AR40XX_EEE_CTRL_DISABLE_PHY(_i) BIT(4 + (_i) * 2) + +#define AR40XX_REG_PORT_VLAN0(_i) (0x420 + (_i) * 0x8) +#define AR40XX_PORT_VLAN0_DEF_SVID BITS(0, 12) +#define AR40XX_PORT_VLAN0_DEF_SVID_S 0 +#define AR40XX_PORT_VLAN0_DEF_CVID BITS(16, 12) +#define AR40XX_PORT_VLAN0_DEF_CVID_S 16 + +#define AR40XX_REG_PORT_VLAN1(_i) (0x424 + (_i) * 0x8) +#define AR40XX_PORT_VLAN1_CORE_PORT BIT(9) +#define AR40XX_PORT_VLAN1_PORT_TLS_MODE BIT(7) +#define AR40XX_PORT_VLAN1_PORT_VLAN_PROP BIT(6) +#define AR40XX_PORT_VLAN1_OUT_MODE BITS(12, 2) +#define AR40XX_PORT_VLAN1_OUT_MODE_S 12 +#define AR40XX_PORT_VLAN1_OUT_MODE_UNMOD 0 +#define AR40XX_PORT_VLAN1_OUT_MODE_UNTAG 1 +#define AR40XX_PORT_VLAN1_OUT_MODE_TAG 2 +#define AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH 3 + +#define AR40XX_REG_ATU_DATA0 0x600 +#define AR40XX_ATU_DATA0_MAC_ADDR3 BITS(0, 8) +#define AR40XX_ATU_DATA0_MAC_ADDR3_S 0 +#define AR40XX_ATU_DATA0_MAC_ADDR2 BITS(8, 8) +#define AR40XX_ATU_DATA0_MAC_ADDR2_S 8 +#define AR40XX_ATU_DATA0_MAC_ADDR1 BITS(16, 8) +#define AR40XX_ATU_DATA0_MAC_ADDR1_S 16 +#define AR40XX_ATU_DATA0_MAC_ADDR0 BITS(24, 8) +#define AR40XX_ATU_DATA0_MAC_ADDR0_S 24 + +#define AR40XX_REG_ATU_DATA1 0x604 +#define AR40XX_ATU_DATA1_MAC_ADDR4 BITS(0, 8) +#define AR40XX_ATU_DATA1_MAC_ADDR4_S 0 +#define AR40XX_ATU_DATA1_MAC_ADDR5 BITS(8, 8) +#define AR40XX_ATU_DATA1_MAC_ADDR5_S 8 +#define AR40XX_ATU_DATA1_DEST_PORT BITS(16, 7) +#define AR40XX_ATU_DATA1_DEST_PORT_S 16 +#define AR40XX_ATU_DATA1_CROSS_PORT_STATE_EN BIT(23) +#define AR40XX_ATU_DATA1_PRI BITS(24, 3) +#define AR40XX_ATU_DATA1_SVL_ENTRY BIT(27) +#define AR40XX_ATU_DATA1_PRI_OVER_EN BIT(28) +#define AR40XX_ATU_DATA1_MIRROR_EN BIT(29) +#define AR40XX_ATU_DATA1_SA_DROP_EN BIT(30) +#define AR40XX_ATU_DATA1_HASH_HIGH_ADDR BIT(31) + +#define AR40XX_REG_ATU_DATA2 0x608 +#define AR40XX_ATU_FUNC_DATA2_STATUS BITS(0, 4) +#define AR40XX_ATU_FUNC_DATA2_STATUS_S 0 +#define AR40XX_ATU_FUNC_DATA2_VLAN_LEAKY_EN BIT(4) +#define AR40XX_ATU_FUNC_DATA2_REDIRECT_TO_CPU BIT(5) +#define AR40XX_ATU_FUNC_DATA2_COPY_TO_CPU BIT(6) +#define AR40XX_ATU_FUNC_DATA2_SHORT_LOOP BIT(7) +#define AR40XX_ATU_FUNC_DATA2_ATU_VID BITS(8, 12) +#define AR40XX_ATU_FUNC_DATA2_ATU_VID_S 8 + +#define AR40XX_REG_ATU_FUNC 0x60c +#define AR40XX_ATU_FUNC_OP BITS(0, 4) +#define AR40XX_ATU_FUNC_OP_NOOP 0x0 +#define AR40XX_ATU_FUNC_OP_FLUSH 0x1 +#define AR40XX_ATU_FUNC_OP_LOAD 0x2 +#define AR40XX_ATU_FUNC_OP_PURGE 0x3 +#define AR40XX_ATU_FUNC_OP_FLUSH_LOCKED 0x4 +#define AR40XX_ATU_FUNC_OP_FLUSH_UNICAST 0x5 +#define AR40XX_ATU_FUNC_OP_GET_NEXT 0x6 +#define AR40XX_ATU_FUNC_OP_SEARCH_MAC 0x7 +#define AR40XX_ATU_FUNC_OP_CHANGE_TRUNK 0x8 +#define AR40XX_ATU_FUNC_PORT_NUM BITS(8, 4) +#define AR40XX_ATU_FUNC_PORT_NUM_S 8 +#define AR40XX_ATU_FUNC_BUSY BIT(31) + + + +#define AR40XX_REG_VTU_FUNC0 0x0610 +#define AR40XX_VTU_FUNC0_EG_MODE BITS(4, 14) +#define AR40XX_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2) +#define AR40XX_VTU_FUNC0_EG_MODE_KEEP 0 +#define AR40XX_VTU_FUNC0_EG_MODE_UNTAG 1 +#define AR40XX_VTU_FUNC0_EG_MODE_TAG 2 +#define AR40XX_VTU_FUNC0_EG_MODE_NOT 3 +#define AR40XX_VTU_FUNC0_IVL BIT(19) +#define AR40XX_VTU_FUNC0_VALID BIT(20) + +#define AR40XX_REG_VTU_FUNC1 0x0614 +#define AR40XX_VTU_FUNC1_OP BITS(0, 3) +#define AR40XX_VTU_FUNC1_OP_NOOP 0 +#define AR40XX_VTU_FUNC1_OP_FLUSH 1 +#define AR40XX_VTU_FUNC1_OP_LOAD 2 +#define AR40XX_VTU_FUNC1_OP_PURGE 3 +#define AR40XX_VTU_FUNC1_OP_REMOVE_PORT 4 +#define AR40XX_VTU_FUNC1_OP_GET_NEXT 5 +#define AR40XX_VTU_FUNC1_OP_GET_ONE 6 +#define AR40XX_VTU_FUNC1_FULL BIT(4) +#define AR40XX_VTU_FUNC1_PORT BIT(8, 4) +#define AR40XX_VTU_FUNC1_PORT_S 8 +#define AR40XX_VTU_FUNC1_VID BIT(16, 12) +#define AR40XX_VTU_FUNC1_VID_S 16 +#define AR40XX_VTU_FUNC1_BUSY BIT(31) + +#define AR40XX_REG_FWD_CTRL0 0x620 +#define AR40XX_FWD_CTRL0_CPU_PORT_EN BIT(10) +#define AR40XX_FWD_CTRL0_MIRROR_PORT BITS(4, 4) +#define AR40XX_FWD_CTRL0_MIRROR_PORT_S 4 + +#define AR40XX_REG_FWD_CTRL1 0x624 +#define AR40XX_FWD_CTRL1_UC_FLOOD BITS(0, 7) +#define AR40XX_FWD_CTRL1_UC_FLOOD_S 0 +#define AR40XX_FWD_CTRL1_MC_FLOOD BITS(8, 7) +#define AR40XX_FWD_CTRL1_MC_FLOOD_S 8 +#define AR40XX_FWD_CTRL1_BC_FLOOD BITS(16, 7) +#define AR40XX_FWD_CTRL1_BC_FLOOD_S 16 +#define AR40XX_FWD_CTRL1_IGMP BITS(24, 7) +#define AR40XX_FWD_CTRL1_IGMP_S 24 + +#define AR40XX_REG_PORT_LOOKUP(_i) (0x660 + (_i) * 0xc) +#define AR40XX_PORT_LOOKUP_MEMBER BITS(0, 7) +#define AR40XX_PORT_LOOKUP_IN_MODE BITS(8, 2) +#define AR40XX_PORT_LOOKUP_IN_MODE_S 8 +#define AR40XX_PORT_LOOKUP_STATE BITS(16, 3) +#define AR40XX_PORT_LOOKUP_STATE_S 16 +#define AR40XX_PORT_LOOKUP_LEARN BIT(20) +#define AR40XX_PORT_LOOKUP_LOOPBACK BIT(21) +#define AR40XX_PORT_LOOKUP_ING_MIRROR_EN BIT(25) + +#define AR40XX_REG_QM_DEBUG_ADDR 0x820 +#define AR40XX_REG_QM_DEBUG_VALUE 0x824 +#define AR40XX_REG_QM_PORT0_3_QNUM 0x1d +#define AR40XX_REG_QM_PORT4_6_QNUM 0x1e + +#define AR40XX_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) +#define AR40XX_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) + +#define AR40XX_REG_PORT_FLOWCTRL_THRESH(_i) (0x9b0 + (_i) * 0x4) +#define AR40XX_PORT0_FC_THRESH_ON_DFLT 0x60 +#define AR40XX_PORT0_FC_THRESH_OFF_DFLT 0x90 + +#define AR40XX_PHY_DEBUG_0 0 +#define AR40XX_PHY_MANU_CTRL_EN BIT(12) + +#define AR40XX_PHY_DEBUG_2 2 + +#define AR40XX_PHY_SPEC_STATUS 0x11 +#define AR40XX_PHY_SPEC_STATUS_LINK BIT(10) +#define AR40XX_PHY_SPEC_STATUS_DUPLEX BIT(13) +#define AR40XX_PHY_SPEC_STATUS_SPEED BITS(14, 2) + +/* port forwarding state */ +enum { + AR40XX_PORT_STATE_DISABLED = 0, + AR40XX_PORT_STATE_BLOCK = 1, + AR40XX_PORT_STATE_LISTEN = 2, + AR40XX_PORT_STATE_LEARN = 3, + AR40XX_PORT_STATE_FORWARD = 4 +}; + +/* ingress 802.1q mode */ +enum { + AR40XX_IN_PORT_ONLY = 0, + AR40XX_IN_PORT_FALLBACK = 1, + AR40XX_IN_VLAN_ONLY = 2, + AR40XX_IN_SECURE = 3 +}; + +/* egress 802.1q mode */ +enum { + AR40XX_OUT_KEEP = 0, + AR40XX_OUT_STRIP_VLAN = 1, + AR40XX_OUT_ADD_VLAN = 2 +}; + +/* port speed */ +enum { + AR40XX_PORT_SPEED_10M = 0, + AR40XX_PORT_SPEED_100M = 1, + AR40XX_PORT_SPEED_1000M = 2, + AR40XX_PORT_SPEED_ERR = 3, +}; + +#define AR40XX_MIB_WORK_DELAY 2000 /* msecs */ + +#define AR40XX_QM_WORK_DELAY 100 + +#define AR40XX_MIB_FUNC_CAPTURE 0x3 + +#define AR40XX_REG_PORT_STATS_START 0x1000 +#define AR40XX_REG_PORT_STATS_LEN 0x100 + +#define AR40XX_PORTS_ALL 0x3f + +#define AR40XX_PSGMII_ID 5 +#define AR40XX_PSGMII_CALB_NUM 100 +#define AR40XX_MALIBU_PSGMII_MODE_CTRL 0x6d +#define AR40XX_MALIBU_PHY_PSGMII_MODE_CTRL_ADJUST_VAL 0x220c +#define AR40XX_MALIBU_PHY_MMD7_DAC_CTRL 0x801a +#define AR40XX_MALIBU_DAC_CTRL_MASK 0x380 +#define AR40XX_MALIBU_DAC_CTRL_VALUE 0x280 +#define AR40XX_MALIBU_PHY_RLP_CTRL 0x805a +#define AR40XX_PSGMII_TX_DRIVER_1_CTRL 0xb +#define AR40XX_MALIBU_PHY_PSGMII_REDUCE_SERDES_TX_AMP 0x8a +#define AR40XX_MALIBU_PHY_LAST_ADDR 4 + +#endif /* __AR40XX_REG_H__ */ Index: sys/dev/etherswitch/ar40xx/ar40xx_var.h =================================================================== --- /dev/null +++ sys/dev/etherswitch/ar40xx/ar40xx_var.h @@ -0,0 +1,137 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AR40XX_VAR_H__ +#define __AR40XX_VAR_H__ + +#define AR40XX_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AR40XX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AR40XX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define AR40XX_REG_WRITE(sc, reg, val) do { \ + bus_write_4(sc->sc_ess_mem_res, (reg), (val)); \ + } while (0) + +#define AR40XX_REG_READ(sc, reg) bus_read_4(sc->sc_ess_mem_res, (reg)) + +#define AR40XX_REG_BARRIER_WRITE(sc) bus_barrier((sc)->sc_ess_mem_res, \ + 0, (sc)->sc_ess_mem_size, BUS_SPACE_BARRIER_WRITE) +#define AR40XX_REG_BARRIER_READ(sc) bus_barrier((sc)->sc_ess_mem_res, \ + 0, (sc)->sc_ess_mem_size, BUS_SPACE_BARRIER_READ) +#define AR40XX_REG_BARRIER_RW(sc) bus_barrier((sc)->sc_ess_mem_res, \ + 0, (sc)->sc_ess_mem_size, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + +/* Size of the VLAN table itself in hardware */ +#define AR40XX_NUM_VTU_ENTRIES 64 +#define AR40XX_NUM_PORTS 6 +#define AR40XX_NUM_PHYS 5 +/* Size of the ATU table in hardware */ +#define AR40XX_NUM_ATU_ENTRIES 2048 + +struct ar40xx_softc { + struct mtx sc_mtx; /* serialize access to softc */ + device_t sc_dev; + uint32_t sc_debug; + + /* ess-switch memory resource */ + struct resource *sc_ess_mem_res; + int sc_ess_mem_rid; + size_t sc_ess_mem_size; + + /* ess-switch clock resource */ + clk_t sc_ess_clk; + + /* ess-switch reset resource */ + hwreset_t sc_ess_rst; + + /* phy update callout timer */ + struct callout sc_phy_callout; + + /* memory for the ess-psgmii config interface */ + bus_space_tag_t sc_psgmii_mem_tag; + bus_space_handle_t sc_psgmii_mem_handle; + bus_size_t sc_psgmii_mem_size; + + /* reference to the ipq4019-mdio interface */ + phandle_t sc_mdio_phandle; + device_t sc_mdio_dev; + + etherswitch_info_t sc_info; + + struct { + uint32_t phy_t_status; + } sc_psgmii; + + struct { + uint32_t switch_mac_mode; + uint32_t switch_cpu_bmp; + uint32_t switch_lan_bmp; + uint32_t switch_wan_bmp; + } sc_config; + + /* VLAN table configuration */ + struct { + /* Whether 802.1q VLANs are enabled or not */ + bool vlan; + /* Map etherswitch vgroup to 802.1q vlan */ + uint16_t vlan_id[AR40XX_NUM_VTU_ENTRIES]; + /* VLAN port membership */ + uint8_t vlan_ports[AR40XX_NUM_VTU_ENTRIES]; + /* VLAN port membership - untagged ports */ + uint16_t vlan_untagged[AR40XX_NUM_VTU_ENTRIES]; + /* PVID for each port - index into vlan_id[] */ + uint16_t pvid[AR40XX_NUM_PORTS]; + } sc_vlan; + + struct { + bool mirror_rx; + bool mirror_tx; + int source_port; + int monitor_port; + } sc_monitor; + + struct { + char *ifname[AR40XX_NUM_PHYS]; + device_t miibus[AR40XX_NUM_PHYS]; + struct ifnet *ifp[AR40XX_NUM_PHYS]; + } sc_phys; + + /* ATU (address table unit) support */ + struct { + int count; + int size; + etherswitch_atu_entry_t entries[AR40XX_NUM_ATU_ENTRIES]; + } atu; +}; + +#endif /* __AR40XX_VAR_H__ */ + Index: sys/dev/qcom_mdio/qcom_mdio_debug.h =================================================================== --- /dev/null +++ sys/dev/qcom_mdio/qcom_mdio_debug.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef __QCOM_MDIO_DEBUG_H__ +#define __QCOM_MDIO_DEBUG_H__ + +#define QCOM_MDIO_DEBUG_REG_READ 0x00000001 +#define QCOM_MDIO_DEBUG_REG_WRITE 0x00000001 + +#define QCOM_MDIO_DPRINTF(sc, flags, ...) \ + do { \ + if ((sc)->sc_debug & (flags)) \ + device_printf((sc)->sc_dev, __VA_ARGS__); \ + } while (0) + +#endif /* __QCOM_MDIO_DEBUG_H__ */ Index: sys/dev/qcom_mdio/qcom_mdio_ipq4018.c =================================================================== --- /dev/null +++ sys/dev/qcom_mdio/qcom_mdio_ipq4018.c @@ -0,0 +1,300 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd + * + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +/* + * This is the MDIO controller for the IPQ4018/IPQ4019. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "mdio_if.h" + +#include +#include + +#include + +static int +qcom_mdio_ipq4018_probe(device_t dev) +{ + + if (! ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "qcom,ipq4019-mdio") == 0) + return (ENXIO); + + device_set_desc(dev, + "Qualcomm Atheros IPQ4018/IPQ4019 MDIO driver"); + return (0); +} + +static int +qcom_mdio_ipq4018_detach(device_t dev) +{ + struct qcom_mdio_ipq4018_softc *sc = device_get_softc(dev); + + if (sc->sc_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +qcom_mdio_sysctl_attach(struct qcom_mdio_ipq4018_softc *sc) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, + "control debugging printfs"); +} + +static int +qcom_mdio_ipq4018_attach(device_t dev) +{ + phandle_t node; + struct qcom_mdio_ipq4018_softc *sc = device_get_softc(dev); + int error = 0; + + node = ofw_bus_get_node(dev); + + sc->sc_dev = dev; + sc->sc_debug = 0; + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* + * Map the MDIO memory region. + */ + sc->sc_mem_rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + error = ENXIO; + device_printf(dev, "%s: failed to map device memory\n", + __func__); + goto error; + } + sc->sc_mem_res_size = (size_t) bus_get_resource_count(dev, + SYS_RES_MEMORY, sc->sc_mem_rid); + if (sc->sc_mem_res_size == 0) { + error = ENXIO; + device_printf(dev, "%s: failed to get device memory size\n", + __func__); + goto error; + + } + + qcom_mdio_sysctl_attach(sc); + + OF_device_register_xref(OF_xref_from_node(node), dev); + + return (0); +error: + if (sc->sc_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + + mtx_destroy(&sc->sc_mtx); + return (error); +} + +/* + * Wait for the BUSY flag to become zero. + * + * This has to happen before every MDIO transfer. + * + * Returns 0 if OK, error if error/timed out. + */ +static int +qcom_mdio_ipq4018_wait(struct qcom_mdio_ipq4018_softc *sc) +{ + int i; + uint32_t reg; + + MDIO_LOCK_ASSERT(sc); + + for (i = 0; i < QCOM_IPQ4018_MDIO_SLEEP_COUNT; i++) { + MDIO_BARRIER_READ(sc); + + reg = MDIO_READ(sc, QCOM_IPQ4018_MDIO_REG_CMD); + if ((reg & QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_BUSY) == 0) + return (0); + DELAY(QCOM_IPQ4018_MDIO_SLEEP); + } + device_printf(sc->sc_dev, "%s: warning: timeout waiting for bus\n", + __func__); + return (ETIMEDOUT); +} + +static void +qcom_mdio_ipq4018_set_phy_reg_addr(struct qcom_mdio_ipq4018_softc *sc, + int phy, int reg) +{ + + MDIO_LOCK_ASSERT(sc); + + MDIO_WRITE(sc, QCOM_IPQ4018_MDIO_REG_ADDR, + ((phy & 0xff) << 8) | (reg & 0xff)); + MDIO_BARRIER_WRITE(sc); +} + +static int +qcom_mdio_ipq4018_readreg(device_t dev, int phy, int reg) +{ + struct qcom_mdio_ipq4018_softc *sc = device_get_softc(dev); + uint32_t ret; + + QCOM_MDIO_DPRINTF(sc, QCOM_MDIO_DEBUG_REG_READ, + "%s: called; phy=0x%x reg=0x%x\n", + __func__, phy, reg); + + MDIO_LOCK(sc); + if (qcom_mdio_ipq4018_wait(sc) != 0) { + MDIO_UNLOCK(sc); + return (-1); + } + + /* Set phy/reg values */ + qcom_mdio_ipq4018_set_phy_reg_addr(sc, phy, reg); + + /* Issue read command */ + MDIO_WRITE(sc, QCOM_IPQ4018_MDIO_REG_CMD, + QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_START | + QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_CODE_READ); + MDIO_BARRIER_WRITE(sc); + + /* Wait for completion */ + if (qcom_mdio_ipq4018_wait(sc) != 0) { + MDIO_UNLOCK(sc); + return (-1); + } + + /* Fetch return register value */ + MDIO_BARRIER_READ(sc); + ret = MDIO_READ(sc, QCOM_IPQ4018_MDIO_REG_READ); + MDIO_UNLOCK(sc); + + QCOM_MDIO_DPRINTF(sc, QCOM_MDIO_DEBUG_REG_READ, + "%s: -> 0x%x\n", __func__, ret); + + return (ret); +} + +static int +qcom_mdio_ipq4018_writereg(device_t dev, int phy, int reg, int value) +{ + struct qcom_mdio_ipq4018_softc *sc = device_get_softc(dev); + + QCOM_MDIO_DPRINTF(sc, QCOM_MDIO_DEBUG_REG_WRITE, + "%s: called; phy=0x%x reg=0x%x val=0x%x\n", + __func__, phy, reg, value); + + MDIO_LOCK(sc); + if (qcom_mdio_ipq4018_wait(sc) != 0) { + MDIO_UNLOCK(sc); + return (-1); + } + + /* Set phy/reg values */ + qcom_mdio_ipq4018_set_phy_reg_addr(sc, phy, reg); + + /* Write command */ + MDIO_WRITE(sc, QCOM_IPQ4018_MDIO_REG_WRITE, value); + MDIO_BARRIER_WRITE(sc); + + /* Issue write command */ + MDIO_WRITE(sc, QCOM_IPQ4018_MDIO_REG_CMD, + QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_START | + QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_CODE_WRITE); + MDIO_BARRIER_WRITE(sc); + + /* Wait for completion */ + if (qcom_mdio_ipq4018_wait(sc) != 0) { + MDIO_UNLOCK(sc); + return (-1); + } + MDIO_UNLOCK(sc); + + return (0); +} + +static device_method_t qcom_mdio_ipq4018_methods[] = { + /* Driver */ + DEVMETHOD(device_probe, qcom_mdio_ipq4018_probe), + DEVMETHOD(device_attach, qcom_mdio_ipq4018_attach), + DEVMETHOD(device_detach, qcom_mdio_ipq4018_detach), + + /* MDIO interface */ + DEVMETHOD(mdio_readreg, qcom_mdio_ipq4018_readreg), + DEVMETHOD(mdio_writereg, qcom_mdio_ipq4018_writereg), + + {0, 0}, +}; + +static driver_t qcom_mdio_ipq4018_driver = { + "mdio", + qcom_mdio_ipq4018_methods, + sizeof(struct qcom_mdio_ipq4018_softc), +}; +static devclass_t qcom_mdio_ipq4018_devclass; + +EARLY_DRIVER_MODULE(qcom_mdio_ipq4018, simplebus, qcom_mdio_ipq4018_driver, + qcom_mdio_ipq4018_devclass, NULL, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); +EARLY_DRIVER_MODULE(qcom_mdio_ipq4018, ofwbus, qcom_mdio_ipq4018_driver, + qcom_mdio_ipq4018_devclass, NULL, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); + +MODULE_DEPEND(qcom_mdio_ipq4018, ether, 1, 1, 1); +MODULE_DEPEND(qcom_mdio_ipq4018, mdio, 1, 1, 1); +MODULE_DEPEND(qcom_mdio_ipq4018, miibus, 1, 1, 1); + +MODULE_VERSION(qcom_mdio_ipq4018, 1); Index: sys/dev/qcom_mdio/qcom_mdio_ipq4018_reg.h =================================================================== --- /dev/null +++ sys/dev/qcom_mdio/qcom_mdio_ipq4018_reg.h @@ -0,0 +1,47 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + */ + +#ifndef __QCOM_MDIO_IPQ4018_REG_H__ +#define __QCOM_MDIO_IPQ4018_REG_H__ + +#define QCOM_IPQ4018_MDIO_REG_ADDR 0x44 +#define QCOM_IPQ4018_MDIO_REG_WRITE 0x48 +#define QCOM_IPQ4018_MDIO_REG_READ 0x4c +#define QCOM_IPQ4018_MDIO_REG_CMD 0x50 +#define QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_BUSY (1U << 16) +#define QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_START (1U << 8) +#define QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_CODE_READ 0 +#define QCOM_IPQ4018_MDIO_REG_CMD_ACCESS_CODE_WRITE 1 + +#define QCOM_IPQ4018_MDIO_SLEEP_COUNT 100 +#define QCOM_IPQ4018_MDIO_SLEEP 10 /* uSec */ + +#endif /* __QCOM_MDIO_IPQ4018_REG_H__ */ Index: sys/dev/qcom_mdio/qcom_mdio_ipq4018_var.h =================================================================== --- /dev/null +++ sys/dev/qcom_mdio/qcom_mdio_ipq4018_var.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Adrian Chadd . + * + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + */ + +#ifndef __QCOM_MDIO_IPQ4018_VAR_H__ +#define __QCOM_MDIO_IPQ4018_VAR_H__ + +#define MDIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define MDIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define MDIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define MDIO_WRITE(sc, reg, val) do { \ + bus_write_4(sc->sc_mem_res, (reg), (val)); \ + } while (0) + +#define MDIO_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) + +#define MDIO_BARRIER_WRITE(sc) bus_barrier((sc)->sc_mem_res, \ + 0, (sc)->sc_mem_res_size, BUS_SPACE_BARRIER_WRITE) +#define MDIO_BARRIER_READ(sc) bus_barrier((sc)->sc_mem_res, \ + 0, (sc)->sc_mem_res_size, BUS_SPACE_BARRIER_READ) +#define MDIO_BARRIER_RW(sc) bus_barrier((sc)->sc_mem_res, \ + 0, (sc)->sc_mem_res_size, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + +#define MDIO_SET_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, MDIO_READ(sc, (reg)) | (bits)) + +#define MDIO_CLEAR_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, MDIO_READ(sc, (reg)) & ~(bits)) + +struct qcom_mdio_ipq4018_softc { + device_t sc_dev; + struct mtx sc_mtx; + struct resource *sc_mem_res; + size_t sc_mem_res_size; + int sc_mem_rid; + uint32_t sc_debug; +}; + +#endif /* __QCOM_MDIO_IPQ4018_VAR_H__ */ Index: sys/dts/arm/qcom-ipq4018-rt-ac58u.dts =================================================================== --- sys/dts/arm/qcom-ipq4018-rt-ac58u.dts +++ sys/dts/arm/qcom-ipq4018-rt-ac58u.dts @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT #include "qcom-ipq4019.dtsi" +#include "qcom-ipq4019-ethernet.dtsi" + #include #include #include Index: sys/dts/arm/qcom-ipq4019-ethernet.dtsi =================================================================== --- /dev/null +++ sys/dts/arm/qcom-ipq4019-ethernet.dtsi @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + */ + +#include + +/ { + soc { + mdio: mdio@90000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,ipq4019-mdio"; + reg = <0x90000 0x64>; + status = "disabled"; + + ethphy0: ethernet-phy@0 { + reg = <0>; + + qcom,control-dac = ; + }; + + ethphy1: ethernet-phy@1 { + reg = <1>; + + qcom,control-dac = ; + }; + + ethphy2: ethernet-phy@2 { + reg = <2>; + + qcom,control-dac = ; + }; + + ethphy3: ethernet-phy@3 { + reg = <3>; + + qcom,control-dac = ; + }; + + ethphy4: ethernet-phy@4 { + reg = <4>; + + qcom,control-dac = ; + }; + + psgmiiphy: psgmii-phy@5 { + reg = <5>; + + qcom,tx-driver-strength = ; + qcom,psgmii-az; + }; + }; + + ess-switch@c000000 { + compatible = "qcom,ess-switch"; + reg = <0xc000000 0x80000>; + switch_access_mode = "local bus"; + resets = <&gcc ESS_RESET>; + reset-names = "ess_rst"; + clocks = <&gcc GCC_ESS_CLK>; + clock-names = "ess_clk"; + switch_cpu_bmp = <0x1>; + switch_lan_bmp = <0x1e>; + switch_wan_bmp = <0x20>; + switch_mac_mode = <0>; /* PORT_WRAPPER_PSGMII */ + switch_initvlas = <0x7c 0x54>; + status = "disabled"; + }; + + ess-psgmii@98000 { + compatible = "qcom,ess-psgmii"; + reg = <0x98000 0x800>; + psgmii_access_mode = "local bus"; + status = "disabled"; + }; + + edma@c080000 { + compatible = "qcom,ess-edma"; + reg = <0xc080000 0x8000>; + qcom,page-mode = <0>; + qcom,rx_head_buf_size = <1540>; + qcom,mdio_supported; + qcom,poll_required = <1>; + qcom,num_gmac = <2>; + interrupts = <0 65 IRQ_TYPE_EDGE_RISING + 0 66 IRQ_TYPE_EDGE_RISING + 0 67 IRQ_TYPE_EDGE_RISING + 0 68 IRQ_TYPE_EDGE_RISING + 0 69 IRQ_TYPE_EDGE_RISING + 0 70 IRQ_TYPE_EDGE_RISING + 0 71 IRQ_TYPE_EDGE_RISING + 0 72 IRQ_TYPE_EDGE_RISING + 0 73 IRQ_TYPE_EDGE_RISING + 0 74 IRQ_TYPE_EDGE_RISING + 0 75 IRQ_TYPE_EDGE_RISING + 0 76 IRQ_TYPE_EDGE_RISING + 0 77 IRQ_TYPE_EDGE_RISING + 0 78 IRQ_TYPE_EDGE_RISING + 0 79 IRQ_TYPE_EDGE_RISING + 0 80 IRQ_TYPE_EDGE_RISING + 0 240 IRQ_TYPE_EDGE_RISING + 0 241 IRQ_TYPE_EDGE_RISING + 0 242 IRQ_TYPE_EDGE_RISING + 0 243 IRQ_TYPE_EDGE_RISING + 0 244 IRQ_TYPE_EDGE_RISING + 0 245 IRQ_TYPE_EDGE_RISING + 0 246 IRQ_TYPE_EDGE_RISING + 0 247 IRQ_TYPE_EDGE_RISING + 0 248 IRQ_TYPE_EDGE_RISING + 0 249 IRQ_TYPE_EDGE_RISING + 0 250 IRQ_TYPE_EDGE_RISING + 0 251 IRQ_TYPE_EDGE_RISING + 0 252 IRQ_TYPE_EDGE_RISING + 0 253 IRQ_TYPE_EDGE_RISING + 0 254 IRQ_TYPE_EDGE_RISING + 0 255 IRQ_TYPE_EDGE_RISING>; + + status = "disabled"; + + gmac0: gmac0 { + local-mac-address = [00 00 00 00 00 00]; + vlan_tag = <1 0x1f>; + }; + + gmac1: gmac1 { + local-mac-address = [00 00 00 00 00 00]; + qcom,phy_mdio_addr = <4>; + qcom,poll_required = <1>; + qcom,forced_speed = <1000>; + qcom,forced_duplex = <1>; + vlan_tag = <2 0x20>; + }; + }; + }; +}; Index: sys/dts/include/dt-bindings/net/qcom-qca807x.h =================================================================== --- /dev/null +++ sys/dts/include/dt-bindings/net/qcom-qca807x.h @@ -0,0 +1,81 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 Adrian Chadd + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +/* + * DT constants for the Qualcomm QCA807x PHY + */ + +#ifndef _DT_BINDINGS_NET_QCOM_QCA807X_H__ +#define _DT_BINDINGS_NET_QCOM_QCA807X_H__ + +/* + * PSGMII driver configuration. This controls the TX voltage + * used between the SoC and the external PHY over the SERDES + * interface. + * + * The default value is 12 (600mV) + */ +#define PSGMII_QSGMII_TX_DRIVER_140MV 0 +#define PSGMII_QSGMII_TX_DRIVER_160MV 1 +#define PSGMII_QSGMII_TX_DRIVER_180MV 2 +#define PSGMII_QSGMII_TX_DRIVER_200MV 3 +#define PSGMII_QSGMII_TX_DRIVER_220MV 4 +#define PSGMII_QSGMII_TX_DRIVER_240MV 5 +#define PSGMII_QSGMII_TX_DRIVER_260MV 6 +#define PSGMII_QSGMII_TX_DRIVER_280MV 7 +#define PSGMII_QSGMII_TX_DRIVER_300MV 8 +#define PSGMII_QSGMII_TX_DRIVER_320MV 9 +#define PSGMII_QSGMII_TX_DRIVER_400MV 10 +#define PSGMII_QSGMII_TX_DRIVER_500MV 11 +#define PSGMII_QSGMII_TX_DRIVER_600MV 12 + +/* + * These fields control the PHY power saving based on the + * cable length. + * + * 0 - full amplitude, full bias current + * 1 - amplitude follows cable length, half bias current + * 2 - full amplitude, bias current follows cable length + * 3 - both amplitude and bias current follow cable length + * 4 - full amplitude, half bias current + * 5 - amplitude follows cable length, 1/4 bias current + * when cable length < 10m else half bias current + * 6 - full amplitude, bias current follows cable length, + * bias reduced further by half when cable length < 10m + * 7 - amplitude follows cable length, same bias current + * setting as '6' + */ +#define QCA807X_CONTROL_DAC_FULL_VOLT_BIAS 0 +#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS 1 +#define QCA807X_CONTROL_DAC_FULL_VOLT_DSP_BIAS 2 +#define QCA807X_CONTROL_DAC_DSP_VOLT_BIAS 3 +#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS 4 +#define QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS 5 +#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS_SHORT 6 +#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS_SHORT 7 + +#endif /* __DT_BINDINGS_NET_QCOM_QCA807X_H__ */