1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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/clk/clk.h>
55 #include <dev/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
ar40xx_hw_port_init(struct ar40xx_softc * sc,int port)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
ar40xx_hw_port_link_down(struct ar40xx_softc * sc,int port)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
ar40xx_hw_port_link_up(struct ar40xx_softc * sc,int port)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
ar40xx_hw_port_cpuport_setup(struct ar40xx_softc * sc)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
ar40xx_hw_get_port_pvid(struct ar40xx_softc * sc,int port,int * pvid)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
ar40xx_hw_set_port_pvid(struct ar40xx_softc * sc,int port,int pvid)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
ar40xx_hw_port_setup(struct ar40xx_softc * sc,int port,uint32_t members)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