1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/bus.h> 30 #include <sys/errno.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/module.h> 34 #include <sys/socket.h> 35 #include <sys/sockio.h> 36 #include <sys/sysctl.h> 37 #include <sys/systm.h> 38 39 #include <net/if.h> 40 #include <net/if_var.h> 41 #include <net/if_arp.h> 42 #include <net/ethernet.h> 43 #include <net/if_dl.h> 44 #include <net/if_media.h> 45 #include <net/if_types.h> 46 47 #include <machine/bus.h> 48 #include <dev/iicbus/iic.h> 49 #include <dev/iicbus/iiconf.h> 50 #include <dev/iicbus/iicbus.h> 51 #include <dev/mii/mii.h> 52 #include <dev/mii/miivar.h> 53 #include <dev/mdio/mdio.h> 54 #include <dev/extres/clk/clk.h> 55 #include <dev/extres/hwreset/hwreset.h> 56 57 #include <dev/fdt/fdt_common.h> 58 #include <dev/ofw/ofw_bus.h> 59 #include <dev/ofw/ofw_bus_subr.h> 60 61 #include <dev/etherswitch/etherswitch.h> 62 63 #include <dev/etherswitch/ar40xx/ar40xx_var.h> 64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h> 65 #include <dev/etherswitch/ar40xx/ar40xx_debug.h> 66 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h> 67 68 #include "mdio_if.h" 69 #include "miibus_if.h" 70 #include "etherswitch_if.h" 71 72 73 int 74 ar40xx_hw_port_init(struct ar40xx_softc *sc, int port) 75 { 76 uint32_t reg; 77 78 AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, 79 "%s: called; port %d\n", __func__, port); 80 81 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); 82 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HEADER(port), 0); 83 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), 0); 84 AR40XX_REG_BARRIER_WRITE(sc); 85 86 DELAY(20); 87 88 /* 89 * Ok! Here is where things get super fun in the AR40xx 90 * driver in uboot/linux. 91 * 92 * The earlier chipset switch drivers enable auto link enable here. 93 * The switch will poll the PHYs too, and configure appropriately. 94 * 95 * The ar40xx code in linux/u-boot instead has a whole workaround 96 * path that polls things directly and does some weird hijinx. 97 * NOTABLY - they do NOT enable the TX/RX MAC here or autoneg - 98 * it's done in the work around path. 99 * 100 * SO - for now the port is left off until the PHY state changes. 101 * And then we flip it on and off based on the PHY state. 102 */ 103 #if 0 104 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 105 AR40XX_PORT_AUTO_LINK_EN); 106 #endif 107 108 /* 109 * Configure the VLAN egress mode (don't touch them) and 110 * learning state for STP/ATU. This isn't currently 111 * configurable so it's just nailed up here and left alone. 112 */ 113 reg = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH 114 << AR40XX_PORT_VLAN1_OUT_MODE_S; 115 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); 116 117 reg = AR40XX_PORT_LOOKUP_LEARN; 118 reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; 119 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); 120 AR40XX_REG_BARRIER_WRITE(sc); 121 122 return (0); 123 } 124 125 /* 126 * Call when the link for a non-CPU port is down. 127 * 128 * This will turn off the MAC/forwarding path for this port. 129 */ 130 int 131 ar40xx_hw_port_link_down(struct ar40xx_softc *sc, int port) 132 { 133 134 AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, 135 "%s: called; port %d\n", __func__, port); 136 137 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); 138 139 return (0); 140 } 141 142 /* 143 * Call when the link for a non-CPU port is up. 144 * 145 * This will turn on the default auto-link checking and 146 * eventually enable the TX/RX MAC. 147 */ 148 int 149 ar40xx_hw_port_link_up(struct ar40xx_softc *sc, int port) 150 { 151 uint32_t reg; 152 153 AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, 154 "%s: called; port %d\n", __func__, port); 155 156 /* Auto-link enable */ 157 AR40XX_REG_BARRIER_READ(sc); 158 reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(port)); 159 reg |= AR40XX_PORT_AUTO_LINK_EN; 160 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), reg); 161 AR40XX_REG_BARRIER_WRITE(sc); 162 163 return (0); 164 } 165 166 /* 167 * Setup the CPU facing port. For this device it'll only 168 * be port 0. 169 */ 170 int 171 ar40xx_hw_port_cpuport_setup(struct ar40xx_softc *sc) 172 { 173 uint32_t reg; 174 175 AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called\n", 176 __func__); 177 178 reg = AR40XX_PORT_STATUS_TXFLOW 179 | AR40XX_PORT_STATUS_RXFLOW 180 | AR40XX_PORT_TXHALF_FLOW 181 | AR40XX_PORT_DUPLEX 182 | AR40XX_PORT_SPEED_1000M; 183 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); 184 DELAY(20); 185 186 reg |= AR40XX_PORT_TX_EN | AR40XX_PORT_RX_EN; 187 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); 188 AR40XX_REG_BARRIER_WRITE(sc); 189 190 return (0); 191 } 192 193 /* 194 * Fetch the port PVID. 195 * 196 * For 802.1q mode this is the default VLAN ID for the port. 197 * Frames without an 802.1q VLAN will assume this VLAN ID for 198 * transmit/receive. 199 */ 200 int 201 ar40xx_hw_get_port_pvid(struct ar40xx_softc *sc, int port, int *pvid) 202 { 203 uint32_t reg; 204 205 AR40XX_LOCK_ASSERT(sc); 206 207 AR40XX_REG_BARRIER_READ(sc); 208 reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(port)); 209 210 reg = reg >> AR40XX_PORT_VLAN0_DEF_CVID_S; 211 reg = reg & 0x0fff; /* XXX */ 212 213 *pvid = reg; 214 return (0); 215 } 216 217 /* 218 * Set the port PVID. 219 * 220 * For now, since double-tagged frames aren't currently supported, 221 * CVID=SVID here. 222 */ 223 int 224 ar40xx_hw_set_port_pvid(struct ar40xx_softc *sc, int port, int pvid) 225 { 226 uint32_t reg; 227 228 AR40XX_LOCK_ASSERT(sc); 229 230 pvid &= ETHERSWITCH_VID_MASK; 231 232 reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; 233 reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; 234 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); 235 AR40XX_REG_BARRIER_WRITE(sc); 236 237 return (0); 238 } 239 240 /* 241 * Setup the default port membership configuration. 242 * 243 * This configures the PVID for the port in the sc_vlan config, 244 * along with a set of ports that constitute the "membership" 245 * of this particular VID. 246 * 247 * For 802.1q mode the membership can be viewed as the default 248 * learning port group, but this can be added to via VLAN membership. 249 * (Eg you could in theory split two LAN ports into separate "member" 250 * groups and they'd not learn MAC addresses from each other even 251 * inside a VLAN; you'd then end up with the traffic being flooded to 252 * the CPU port.) 253 */ 254 int 255 ar40xx_hw_port_setup(struct ar40xx_softc *sc, int port, uint32_t members) 256 { 257 uint32_t egress, ingress, reg; 258 uint32_t pvid = sc->sc_vlan.vlan_id[sc->sc_vlan.pvid[port]] 259 & ETHERSWITCH_VID_MASK; 260 261 if (sc->sc_vlan.vlan) { 262 egress = AR40XX_PORT_VLAN1_OUT_MODE_UNMOD; 263 ingress = AR40XX_IN_SECURE; 264 } else { 265 egress = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH; 266 ingress = AR40XX_IN_PORT_ONLY; 267 } 268 269 reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; 270 reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; 271 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); 272 AR40XX_REG_BARRIER_WRITE(sc); 273 274 reg = AR40XX_PORT_VLAN1_PORT_VLAN_PROP; 275 reg |= egress << AR40XX_PORT_VLAN1_OUT_MODE_S; 276 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); 277 AR40XX_REG_BARRIER_WRITE(sc); 278 279 reg = members; 280 reg |= AR40XX_PORT_LOOKUP_LEARN; 281 reg |= ingress << AR40XX_PORT_LOOKUP_IN_MODE_S; 282 reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; 283 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); 284 AR40XX_REG_BARRIER_WRITE(sc); 285 286 return (0); 287 } 288