ar40xx_switch: add initial switch for the IPQ4018/IPQ4019.
Summary: This switch is based off of the AR8327/AR8337 external switch/PHY. However unlike the AR8327/AR8337 it itself doesn't have any PHYs; instead an external PHY connects to it using the PSGMII port. Differential Revision: https://reviews.freebsd.org/D34112 Reviewed by: manu This code is inspired by the ar40xx code in openwrt, which itself is based on the Qualcomm QCA-SSDK. Both of these sources are, amusingly, BSD licenced - and thus I have included some of the comments in the hardware workaround paths to document some of the magic numbers.
This commit is contained in:
parent
b509e53896
commit
e388de98bd
@ -78,3 +78,5 @@ device mdio
|
||||
device mii
|
||||
device miibus
|
||||
device qcom_mdio_ipq4018
|
||||
device etherswitch
|
||||
device ar40xx_switch
|
||||
|
@ -6,6 +6,27 @@ arm/qualcomm/qcom_cpu_kpssv2.c optional smp
|
||||
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
|
||||
|
44
sys/dev/etherswitch/ar40xx/ar40xx_debug.h
Normal file
44
sys/dev/etherswitch/ar40xx/ar40xx_debug.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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__ */
|
357
sys/dev/etherswitch/ar40xx/ar40xx_hw.c
Normal file
357
sys/dev/etherswitch/ar40xx/ar40xx_hw.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
|
||||
/*
|
||||
* XXX these are here for now; move the code using these
|
||||
* into main.c once this is all done!
|
||||
*/
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mirror.h>
|
||||
|
||||
#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);
|
||||
}
|
42
sys/dev/etherswitch/ar40xx/ar40xx_hw.h
Normal file
42
sys/dev/etherswitch/ar40xx/ar40xx_hw.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
216
sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c
Normal file
216
sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
|
||||
#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);
|
||||
}
|
37
sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.h
Normal file
37
sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
129
sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c
Normal file
129
sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c
Normal file
@ -0,0 +1,129 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
40
sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.h
Normal file
40
sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
194
sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c
Normal file
194
sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c
Normal file
@ -0,0 +1,194 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mib.h>
|
||||
|
||||
#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);
|
||||
}
|
36
sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.h
Normal file
36
sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
132
sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c
Normal file
132
sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c
Normal file
@ -0,0 +1,132 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mirror.h>
|
||||
|
||||
#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);
|
||||
}
|
33
sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.h
Normal file
33
sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
287
sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c
Normal file
287
sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c
Normal file
@ -0,0 +1,287 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
|
||||
|
||||
#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);
|
||||
}
|
42
sys/dev/etherswitch/ar40xx/ar40xx_hw_port.h
Normal file
42
sys/dev/etherswitch/ar40xx/ar40xx_hw_port.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
437
sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c
Normal file
437
sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c
Normal file
@ -0,0 +1,437 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_phy.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
|
||||
|
||||
#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);
|
||||
}
|
41
sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h
Normal file
41
sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
196
sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c
Normal file
196
sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
39
sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.h
Normal file
39
sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
968
sys/dev/etherswitch/ar40xx/ar40xx_main.c
Normal file
968
sys/dev/etherswitch/ar40xx/ar40xx_main.c
Normal file
@ -0,0 +1,968 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_phy.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mib.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
|
||||
|
||||
#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);
|
252
sys/dev/etherswitch/ar40xx/ar40xx_phy.c
Normal file
252
sys/dev/etherswitch/ar40xx/ar40xx_phy.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <dev/iicbus/iic.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/mii/mii.h>
|
||||
#include <dev/mii/miivar.h>
|
||||
#include <dev/mdio/mdio.h>
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/etherswitch/etherswitch.h>
|
||||
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_var.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_reg.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_phy.h>
|
||||
#include <dev/etherswitch/ar40xx/ar40xx_debug.h>
|
||||
|
||||
#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);
|
||||
}
|
39
sys/dev/etherswitch/ar40xx/ar40xx_phy.h
Normal file
39
sys/dev/etherswitch/ar40xx/ar40xx_phy.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
348
sys/dev/etherswitch/ar40xx/ar40xx_reg.h
Normal file
348
sys/dev/etherswitch/ar40xx/ar40xx_reg.h
Normal file
@ -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__ */
|
135
sys/dev/etherswitch/ar40xx/ar40xx_var.h
Normal file
135
sys/dev/etherswitch/ar40xx/ar40xx_var.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user