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