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_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
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
132 ar40xx_portforphy(int phy)
133 {
134
135 return (phy+1);
136 }
137
138 struct mii_data *
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
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
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, ifp->if_dunit); /* XXX - DRVAPI */
166
167 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
168 __func__, ifp->if_dunit); /* XXX - DRVAPI */
169
170 if (mii == NULL)
171 return (ENXIO);
172 mii_mediachg(mii);
173 return (0);
174 }
175
176 static void
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, ifp->if_dunit); /* XXX - DRVAPI */
181
182 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
183 __func__, ifp->if_dunit); /* XXX - DRVAPI */
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
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 (sc->sc_phys.ifp[phy] == NULL) {
204 device_printf(sc->sc_dev,
205 "PHY %d: couldn't allocate ifnet structure\n",
206 phy);
207 err = ENOMEM;
208 break;
209 }
210
211 sc->sc_phys.ifp[phy]->if_softc = sc;
212 sc->sc_phys.ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
213 IFF_DRV_RUNNING | IFF_SIMPLEX;
214 sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF,
215 M_WAITOK);
216 bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1);
217 if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy],
218 ar40xx_portforphy(phy));
219 err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy],
220 sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd,
221 ar40xx_ifmedia_sts, BMSR_DEFCAPMASK,
222 phy, MII_OFFSET_ANY, 0);
223 device_printf(sc->sc_dev,
224 "%s attached to pseudo interface %s\n",
225 device_get_nameunit(sc->sc_phys.miibus[phy]),
226 sc->sc_phys.ifp[phy]->if_xname);
227 if (err != 0) {
228 device_printf(sc->sc_dev,
229 "attaching PHY %d failed\n",
230 phy);
231 return (err);
232 }
233 }
234 return (0);
235 }
236
237 int
238 ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc)
239 {
240 int phy;
241 uint32_t id1, id2;
242
243 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
244 id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2);
245 id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3);
246 device_printf(sc->sc_dev,
247 "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n",
248 __func__, phy, id1, id2);
249 }
250
251 return (0);
252 }
Cache object: e76d391860e86021da58233c4b829502
|