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_hw.h>
66 #include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
67 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
68 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
69 #include <dev/etherswitch/ar40xx/ar40xx_phy.h>
70 #include <dev/etherswitch/ar40xx/ar40xx_debug.h>
71
72 #include "mdio_if.h"
73 #include "miibus_if.h"
74 #include "etherswitch_if.h"
75
76
77 int
ar40xx_phy_tick(struct ar40xx_softc * sc)78 ar40xx_phy_tick(struct ar40xx_softc *sc)
79 {
80 struct mii_softc *miisc;
81 struct mii_data *mii;
82 int phy;
83 uint32_t reg;
84
85 AR40XX_LOCK_ASSERT(sc);
86
87 AR40XX_REG_BARRIER_READ(sc);
88 /*
89 * Loop over; update phy port status here
90 */
91 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
92 /*
93 * Port here is PHY, not port!
94 */
95 reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(phy + 1));
96
97 mii = device_get_softc(sc->sc_phys.miibus[phy]);
98
99 /*
100 * Compare the current link status to the previous link
101 * status. We may need to clear ATU / change phy config.
102 */
103 if (((reg & AR40XX_PORT_STATUS_LINK_UP) != 0) &&
104 (mii->mii_media_status & IFM_ACTIVE) == 0) {
105 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS,
106 "%s: PHY %d: down -> up\n", __func__, phy);
107 ar40xx_hw_port_link_up(sc, phy + 1);
108 ar40xx_hw_atu_flush_port(sc, phy + 1);
109 }
110 if (((reg & AR40XX_PORT_STATUS_LINK_UP) == 0) &&
111 (mii->mii_media_status & IFM_ACTIVE) != 0) {
112 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS,
113 "%s: PHY %d: up -> down\n", __func__, phy);
114 ar40xx_hw_port_link_down(sc, phy + 1);
115 ar40xx_hw_atu_flush_port(sc, phy + 1);
116 }
117
118 mii_tick(mii);
119 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
120 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
121 miisc->mii_inst)
122 continue;
123 ukphy_status(miisc);
124 mii_phy_update(miisc, MII_POLLSTAT);
125 }
126 }
127
128 return (0);
129 }
130
131 static inline int
ar40xx_portforphy(int phy)132 ar40xx_portforphy(int phy)
133 {
134
135 return (phy+1);
136 }
137
138 struct mii_data *
ar40xx_phy_miiforport(struct ar40xx_softc * sc,int port)139 ar40xx_phy_miiforport(struct ar40xx_softc *sc, int port)
140 {
141 int phy;
142
143 phy = port-1;
144
145 if (phy < 0 || phy >= AR40XX_NUM_PHYS)
146 return (NULL);
147 return (device_get_softc(sc->sc_phys.miibus[phy]));
148 }
149
150 if_t
ar40xx_phy_ifpforport(struct ar40xx_softc * sc,int port)151 ar40xx_phy_ifpforport(struct ar40xx_softc *sc, int port)
152 {
153 int phy;
154
155 phy = port-1;
156 if (phy < 0 || phy >= AR40XX_NUM_PHYS)
157 return (NULL);
158 return (sc->sc_phys.ifp[phy]);
159 }
160
161 static int
ar40xx_ifmedia_upd(if_t ifp)162 ar40xx_ifmedia_upd(if_t ifp)
163 {
164 struct ar40xx_softc *sc = if_getsoftc(ifp);
165 struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
166
167 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
168 __func__, if_getdunit(ifp));
169
170 if (mii == NULL)
171 return (ENXIO);
172 mii_mediachg(mii);
173 return (0);
174 }
175
176 static void
ar40xx_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)177 ar40xx_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
178 {
179 struct ar40xx_softc *sc = if_getsoftc(ifp);
180 struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
181
182 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
183 __func__, if_getdunit(ifp));
184
185 if (mii == NULL)
186 return;
187 mii_pollstat(mii);
188
189 ifmr->ifm_active = mii->mii_media_active;
190 ifmr->ifm_status = mii->mii_media_status;
191 }
192
193 int
ar40xx_attach_phys(struct ar40xx_softc * sc)194 ar40xx_attach_phys(struct ar40xx_softc *sc)
195 {
196 int phy, err = 0;
197 char name[IFNAMSIZ];
198
199 /* PHYs need an interface, so we generate a dummy one */
200 snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
201 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
202 sc->sc_phys.ifp[phy] = if_alloc(IFT_ETHER);
203 if_setsoftc(sc->sc_phys.ifp[phy], sc);
204 if_setflagbits(sc->sc_phys.ifp[phy], IFF_UP | IFF_BROADCAST |
205 IFF_DRV_RUNNING | IFF_SIMPLEX, 0);
206 sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF,
207 M_WAITOK);
208 bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1);
209 if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy],
210 ar40xx_portforphy(phy));
211 err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy],
212 sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd,
213 ar40xx_ifmedia_sts, BMSR_DEFCAPMASK,
214 phy, MII_OFFSET_ANY, 0);
215 device_printf(sc->sc_dev,
216 "%s attached to pseudo interface %s\n",
217 device_get_nameunit(sc->sc_phys.miibus[phy]),
218 if_name(sc->sc_phys.ifp[phy]));
219 if (err != 0) {
220 device_printf(sc->sc_dev,
221 "attaching PHY %d failed\n",
222 phy);
223 return (err);
224 }
225 }
226 return (0);
227 }
228
229 int
ar40xx_hw_phy_get_ids(struct ar40xx_softc * sc)230 ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc)
231 {
232 int phy;
233 uint32_t id1, id2;
234
235 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
236 id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2);
237 id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3);
238 device_printf(sc->sc_dev,
239 "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n",
240 __func__, phy, id1, id2);
241 }
242
243 return (0);
244 }
245