1 /* $OpenBSD: mvmdio.c,v 1.5 2023/09/22 01:10:44 jsg Exp $ */ 2 /* $NetBSD: if_mvneta.c,v 1.41 2015/04/15 10:15:40 hsuenaga Exp $ */ 3 /* 4 * Copyright (c) 2007, 2008, 2013 KIYOHARA Takashi 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/device.h> 32 #include <sys/socket.h> 33 #include <sys/sockio.h> 34 #include <sys/mutex.h> 35 36 #include <machine/bus.h> 37 #include <machine/fdt.h> 38 #include <machine/simplebusvar.h> 39 40 #include <dev/ofw/openfirm.h> 41 #include <dev/ofw/ofw_clock.h> 42 #include <dev/ofw/ofw_pinctrl.h> 43 #include <dev/ofw/ofw_misc.h> 44 #include <dev/ofw/fdt.h> 45 46 #include <dev/fdt/if_mvnetareg.h> 47 48 #include <net/if.h> 49 50 #define MVNETA_READ(sc, reg) \ 51 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 52 #define MVNETA_WRITE(sc, reg, val) \ 53 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 54 55 struct mvmdio_softc { 56 struct simplebus_softc sc_sbus; 57 58 bus_space_tag_t sc_iot; 59 bus_space_handle_t sc_ioh; 60 61 struct mutex sc_mtx; 62 struct mii_bus sc_mii; 63 }; 64 65 static int mvmdio_match(struct device *, void *, void *); 66 static void mvmdio_attach(struct device *, struct device *, void *); 67 68 int mvmdio_smi_readreg(struct device *, int, int); 69 void mvmdio_smi_writereg(struct device *, int, int, int); 70 71 struct cfdriver mvmdio_cd = { 72 NULL, "mvmdio", DV_DULL 73 }; 74 75 const struct cfattach mvmdio_ca = { 76 sizeof (struct mvmdio_softc), mvmdio_match, mvmdio_attach, 77 }; 78 79 static int 80 mvmdio_match(struct device *parent, void *cfdata, void *aux) 81 { 82 struct fdt_attach_args *faa = aux; 83 84 return OF_is_compatible(faa->fa_node, "marvell,orion-mdio"); 85 } 86 87 static void 88 mvmdio_attach(struct device *parent, struct device *self, void *aux) 89 { 90 struct mvmdio_softc *sc = (struct mvmdio_softc *) self; 91 struct fdt_attach_args *faa = aux; 92 93 if (faa->fa_nreg < 1) { 94 printf(": no registers\n"); 95 return; 96 } 97 98 sc->sc_iot = faa->fa_iot; 99 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 100 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 101 printf(": can't map registers\n"); 102 return; 103 } 104 105 pinctrl_byname(faa->fa_node, "default"); 106 clock_enable_all(faa->fa_node); 107 108 mtx_init(&sc->sc_mtx, IPL_NET); 109 110 sc->sc_mii.md_node = faa->fa_node; 111 sc->sc_mii.md_cookie = sc; 112 sc->sc_mii.md_readreg = mvmdio_smi_readreg; 113 sc->sc_mii.md_writereg = mvmdio_smi_writereg; 114 mii_register(&sc->sc_mii); 115 116 simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); 117 } 118 119 int 120 mvmdio_smi_readreg(struct device *dev, int phy, int reg) 121 { 122 struct mvmdio_softc *sc = (struct mvmdio_softc *) dev; 123 uint32_t smi, val; 124 int i; 125 126 mtx_enter(&sc->sc_mtx); 127 128 for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) { 129 DELAY(1); 130 if (!(MVNETA_READ(sc, 0) & MVNETA_SMI_BUSY)) 131 break; 132 } 133 if (i == MVNETA_PHY_TIMEOUT) { 134 printf("%s: SMI busy timeout\n", sc->sc_sbus.sc_dev.dv_xname); 135 mtx_leave(&sc->sc_mtx); 136 return -1; 137 } 138 139 smi = MVNETA_SMI_PHYAD(phy) | MVNETA_SMI_REGAD(reg) 140 | MVNETA_SMI_OPCODE_READ; 141 MVNETA_WRITE(sc, 0, smi); 142 143 for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) { 144 DELAY(1); 145 smi = MVNETA_READ(sc, 0); 146 if (smi & MVNETA_SMI_READVALID) 147 break; 148 } 149 150 mtx_leave(&sc->sc_mtx); 151 152 val = smi & MVNETA_SMI_DATA_MASK; 153 154 return val; 155 } 156 157 void 158 mvmdio_smi_writereg(struct device *dev, int phy, int reg, int val) 159 { 160 struct mvmdio_softc *sc = (struct mvmdio_softc *) dev; 161 uint32_t smi; 162 int i; 163 164 mtx_enter(&sc->sc_mtx); 165 166 for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) { 167 DELAY(1); 168 if (!(MVNETA_READ(sc, 0) & MVNETA_SMI_BUSY)) 169 break; 170 } 171 if (i == MVNETA_PHY_TIMEOUT) { 172 printf("%s: SMI busy timeout\n", sc->sc_sbus.sc_dev.dv_xname); 173 mtx_leave(&sc->sc_mtx); 174 return; 175 } 176 177 smi = MVNETA_SMI_PHYAD(phy) | MVNETA_SMI_REGAD(reg) | 178 MVNETA_SMI_OPCODE_WRITE | (val & MVNETA_SMI_DATA_MASK); 179 MVNETA_WRITE(sc, 0, smi); 180 181 for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) { 182 DELAY(1); 183 if (!(MVNETA_READ(sc, 0) & MVNETA_SMI_BUSY)) 184 break; 185 } 186 187 mtx_leave(&sc->sc_mtx); 188 189 if (i == MVNETA_PHY_TIMEOUT) 190 printf("%s: phy write timed out\n", sc->sc_sbus.sc_dev.dv_xname); 191 } 192