1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 Alstom Group. 5 * Copyright (c) 2021 Semihalf. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * Driver for TI DP83822 Ethernet PHY 32 */ 33 34 #include <sys/cdefs.h> 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/kernel.h> 38 #include <sys/module.h> 39 #include <sys/resource.h> 40 #include <sys/rman.h> 41 #include <sys/socket.h> 42 43 #include <machine/resource.h> 44 45 #include <net/if.h> 46 #include <net/if_media.h> 47 48 #include <dev/mii/mii.h> 49 #include <dev/mii/miivar.h> 50 51 #include "miidevs.h" 52 #include "miibus_if.h" 53 54 #define BIT(x) (1 << (x)) 55 56 #define DP83822_PHYSTS 0x10 57 #define DP83822_PHYSTS_LINK_UP BIT(0) 58 #define DP83822_PHYSTS_SPEED_100 BIT(1) 59 #define DP83822_PHYSTS_FD BIT(2) 60 61 #define DP83822_PHYSCR 0x11 62 #define DP83822_PHYSCR_INT_OE BIT(0) /* Behaviour of INT pin. */ 63 #define DP83822_PHYSCR_INT_EN BIT(1) 64 65 #define DP83822_MISR1 0x12 66 #define DP83822_MISR1_AN_CMPL_EN BIT(2) 67 #define DP83822_MISR1_DP_CHG_EN BIT(3) 68 #define DP83822_MISR1_SPD_CHG_EN BIT(4) 69 #define DP83822_MISR1_LINK_CHG_EN BIT(5) 70 #define DP83822_MISR1_INT_MASK 0xFF 71 #define DP83822_MISR1_INT_STS_SHIFT 8 72 73 #define DP83822_MISR2 0x13 74 #define DP83822_MISR2_AN_ERR_EN BIT(6) 75 #define DP83822_MISR2_INT_MASK 0xFF 76 #define DP83822_MISR2_INT_STS_SHIFT 8 77 78 static int dp_service(struct mii_softc*, struct mii_data*, int); 79 80 struct dp83822_softc { 81 struct mii_softc mii_sc; 82 struct resource *irq_res; 83 void *irq_cookie; 84 }; 85 86 static const struct mii_phydesc dpphys[] = { 87 MII_PHY_DESC(xxTI, DP83822), 88 MII_PHY_END 89 }; 90 91 static const struct mii_phy_funcs dpphy_funcs = { 92 dp_service, 93 ukphy_status, 94 mii_phy_reset 95 }; 96 97 static void 98 dp_intr(void *arg) 99 { 100 struct mii_softc *sc = (struct mii_softc *)arg; 101 uint32_t status; 102 103 status = PHY_READ(sc, DP83822_MISR1); 104 105 if (!((status >> DP83822_MISR1_INT_STS_SHIFT) & 106 (status & DP83822_MISR1_INT_MASK))) 107 return; 108 109 PHY_STATUS(sc); 110 mii_phy_update(sc, MII_MEDIACHG); 111 } 112 113 static int 114 dp_probe(device_t dev) 115 { 116 117 return (mii_phy_dev_probe(dev, dpphys, BUS_PROBE_DEFAULT)); 118 } 119 120 static int 121 dp_attach(device_t dev) 122 { 123 struct dp83822_softc *sc; 124 struct mii_softc *mii_sc; 125 uint32_t value; 126 int error, rid; 127 128 sc = device_get_softc(dev); 129 mii_sc = &sc->mii_sc; 130 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &dpphy_funcs, 1); 131 132 PHY_RESET(mii_sc); 133 134 rid = 0; 135 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 136 if (sc->irq_res == NULL) 137 goto no_irq; 138 139 error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 140 NULL, dp_intr, sc, &sc->irq_cookie); 141 if (error != 0) { 142 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 143 sc->irq_res = NULL; 144 goto no_irq; 145 } 146 147 /* 148 * ACK and unmask all relevant interrupts. 149 * We have a single register that serves the purpose 150 * of both interrupt status and mask. 151 * Interrupts are cleared on read. 152 */ 153 (void)PHY_READ(mii_sc, DP83822_MISR1); 154 value = DP83822_MISR1_AN_CMPL_EN | 155 DP83822_MISR1_DP_CHG_EN | 156 DP83822_MISR1_SPD_CHG_EN | 157 DP83822_MISR1_LINK_CHG_EN; 158 PHY_WRITE(mii_sc, DP83822_MISR1, value); 159 value = PHY_READ(mii_sc, DP83822_PHYSCR); 160 value |= DP83822_PHYSCR_INT_OE | 161 DP83822_PHYSCR_INT_EN; 162 PHY_WRITE(mii_sc, DP83822_PHYSCR, value); 163 164 no_irq: 165 return (0); 166 } 167 168 static int 169 dp_detach(device_t dev) 170 { 171 struct dp83822_softc *sc; 172 173 sc = device_get_softc(dev); 174 175 bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); 176 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 177 178 return (mii_phy_detach(dev)); 179 } 180 181 static int 182 dp_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 183 { 184 185 switch (cmd) { 186 case MII_POLLSTAT: 187 break; 188 case MII_MEDIACHG: 189 mii_phy_setmedia(sc); 190 break; 191 case MII_TICK: 192 if (mii_phy_tick(sc) == EJUSTRETURN) 193 return (0); 194 195 break; 196 } 197 198 PHY_STATUS(sc); 199 mii_phy_update(sc, cmd); 200 return (0); 201 } 202 203 static device_method_t dp_methods[] = { 204 DEVMETHOD(device_probe, dp_probe), 205 DEVMETHOD(device_attach, dp_attach), 206 DEVMETHOD(device_detach, dp_detach), 207 DEVMETHOD_END 208 }; 209 210 static driver_t dp_driver = { 211 "dp83822phy", 212 dp_methods, 213 sizeof(struct dp83822_softc) 214 }; 215 216 DRIVER_MODULE(dp83822phy, miibus, dp_driver, 0, 0); 217