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/param.h> 35 #include <sys/bus.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/resource.h> 39 #include <sys/rman.h> 40 #include <sys/socket.h> 41 42 #include <machine/resource.h> 43 44 #include <net/if.h> 45 #include <net/if_media.h> 46 47 #include <dev/mii/mii.h> 48 #include <dev/mii/miivar.h> 49 50 #include "miidevs.h" 51 #include "miibus_if.h" 52 53 #define BIT(x) (1 << (x)) 54 55 #define DP83822_PHYSTS 0x10 56 #define DP83822_PHYSTS_LINK_UP BIT(0) 57 #define DP83822_PHYSTS_SPEED_100 BIT(1) 58 #define DP83822_PHYSTS_FD BIT(2) 59 60 #define DP83822_PHYSCR 0x11 61 #define DP83822_PHYSCR_INT_OE BIT(0) /* Behaviour of INT pin. */ 62 #define DP83822_PHYSCR_INT_EN BIT(1) 63 64 #define DP83822_MISR1 0x12 65 #define DP83822_MISR1_AN_CMPL_EN BIT(2) 66 #define DP83822_MISR1_DP_CHG_EN BIT(3) 67 #define DP83822_MISR1_SPD_CHG_EN BIT(4) 68 #define DP83822_MISR1_LINK_CHG_EN BIT(5) 69 #define DP83822_MISR1_INT_MASK 0xFF 70 #define DP83822_MISR1_INT_STS_SHIFT 8 71 72 #define DP83822_MISR2 0x13 73 #define DP83822_MISR2_AN_ERR_EN BIT(6) 74 #define DP83822_MISR2_INT_MASK 0xFF 75 #define DP83822_MISR2_INT_STS_SHIFT 8 76 77 static int dp_service(struct mii_softc*, struct mii_data*, int); 78 79 struct dp83822_softc { 80 struct mii_softc mii_sc; 81 struct resource *irq_res; 82 void *irq_cookie; 83 }; 84 85 static const struct mii_phydesc dpphys[] = { 86 MII_PHY_DESC(xxTI, DP83822), 87 MII_PHY_END 88 }; 89 90 static const struct mii_phy_funcs dpphy_funcs = { 91 dp_service, 92 ukphy_status, 93 mii_phy_reset 94 }; 95 96 static void 97 dp_intr(void *arg) 98 { 99 struct mii_softc *sc = (struct mii_softc *)arg; 100 uint32_t status; 101 102 status = PHY_READ(sc, DP83822_MISR1); 103 104 if (!((status >> DP83822_MISR1_INT_STS_SHIFT) & 105 (status & DP83822_MISR1_INT_MASK))) 106 return; 107 108 PHY_STATUS(sc); 109 mii_phy_update(sc, MII_MEDIACHG); 110 } 111 112 static int 113 dp_probe(device_t dev) 114 { 115 116 return (mii_phy_dev_probe(dev, dpphys, BUS_PROBE_DEFAULT)); 117 } 118 119 static int 120 dp_attach(device_t dev) 121 { 122 struct dp83822_softc *sc; 123 struct mii_softc *mii_sc; 124 uint32_t value; 125 int error, rid; 126 127 sc = device_get_softc(dev); 128 mii_sc = &sc->mii_sc; 129 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &dpphy_funcs, 1); 130 131 PHY_RESET(mii_sc); 132 133 rid = 0; 134 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 135 if (sc->irq_res == NULL) 136 goto no_irq; 137 138 error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 139 NULL, dp_intr, sc, &sc->irq_cookie); 140 if (error != 0) { 141 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 142 sc->irq_res = NULL; 143 goto no_irq; 144 } 145 146 /* 147 * ACK and unmask all relevant interrupts. 148 * We have a single register that serves the purpose 149 * of both interrupt status and mask. 150 * Interrupts are cleared on read. 151 */ 152 (void)PHY_READ(mii_sc, DP83822_MISR1); 153 value = DP83822_MISR1_AN_CMPL_EN | 154 DP83822_MISR1_DP_CHG_EN | 155 DP83822_MISR1_SPD_CHG_EN | 156 DP83822_MISR1_LINK_CHG_EN; 157 PHY_WRITE(mii_sc, DP83822_MISR1, value); 158 value = PHY_READ(mii_sc, DP83822_PHYSCR); 159 value |= DP83822_PHYSCR_INT_OE | 160 DP83822_PHYSCR_INT_EN; 161 PHY_WRITE(mii_sc, DP83822_PHYSCR, value); 162 163 no_irq: 164 return (0); 165 } 166 167 static int 168 dp_detach(device_t dev) 169 { 170 struct dp83822_softc *sc; 171 172 sc = device_get_softc(dev); 173 174 bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); 175 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 176 177 return (mii_phy_detach(dev)); 178 } 179 180 static int 181 dp_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 182 { 183 184 switch (cmd) { 185 case MII_POLLSTAT: 186 break; 187 case MII_MEDIACHG: 188 mii_phy_setmedia(sc); 189 break; 190 case MII_TICK: 191 if (mii_phy_tick(sc) == EJUSTRETURN) 192 return (0); 193 194 break; 195 } 196 197 PHY_STATUS(sc); 198 mii_phy_update(sc, cmd); 199 return (0); 200 } 201 202 static device_method_t dp_methods[] = { 203 DEVMETHOD(device_probe, dp_probe), 204 DEVMETHOD(device_attach, dp_attach), 205 DEVMETHOD(device_detach, dp_detach), 206 DEVMETHOD_END 207 }; 208 209 static driver_t dp_driver = { 210 "dp83822phy", 211 dp_methods, 212 sizeof(struct dp83822_softc) 213 }; 214 215 DRIVER_MODULE(dp83822phy, miibus, dp_driver, 0, 0); 216