aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/etherswitch/ar40xx/ar40xx_phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/etherswitch/ar40xx/ar40xx_phy.c')
-rw-r--r--sys/dev/etherswitch/ar40xx/ar40xx_phy.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_phy.c b/sys/dev/etherswitch/ar40xx/ar40xx_phy.c
new file mode 100644
index 000000000000..aa02ef25ac7b
--- /dev/null
+++ b/sys/dev/etherswitch/ar40xx/ar40xx_phy.c
@@ -0,0 +1,244 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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/clk/clk.h>
+#include <dev/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]));
+}
+
+if_t
+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(if_t ifp)
+{
+ struct ar40xx_softc *sc = if_getsoftc(ifp);
+ struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
+
+ AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
+ __func__, if_getdunit(ifp));
+
+ if (mii == NULL)
+ return (ENXIO);
+ mii_mediachg(mii);
+ return (0);
+}
+
+static void
+ar40xx_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
+{
+ struct ar40xx_softc *sc = if_getsoftc(ifp);
+ struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
+
+ AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
+ __func__, if_getdunit(ifp));
+
+ 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_setsoftc(sc->sc_phys.ifp[phy], sc);
+ if_setflagbits(sc->sc_phys.ifp[phy], IFF_UP | IFF_BROADCAST |
+ IFF_DRV_RUNNING | IFF_SIMPLEX, 0);
+ 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]),
+ if_name(sc->sc_phys.ifp[phy]));
+ 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);
+}