1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 }; 91 92 static const struct mii_phy_funcs dpphy_funcs = { 93 dp_service, 94 ukphy_status, 95 mii_phy_reset 96 }; 97 98 static void 99 dp_intr(void *arg) 100 { 101 struct mii_softc *sc = (struct mii_softc *)arg; 102 uint32_t status; 103 104 status = PHY_READ(sc, DP83822_MISR1); 105 106 if (!((status >> DP83822_MISR1_INT_STS_SHIFT) & 107 (status & DP83822_MISR1_INT_MASK))) 108 return; 109 110 PHY_STATUS(sc); 111 mii_phy_update(sc, MII_MEDIACHG); 112 } 113 114 static int 115 dp_probe(device_t dev) 116 { 117 118 return (mii_phy_dev_probe(dev, dpphys, BUS_PROBE_DEFAULT)); 119 } 120 121 static int 122 dp_attach(device_t dev) 123 { 124 struct dp83822_softc *sc; 125 struct mii_softc *mii_sc; 126 uint32_t value; 127 int error, rid; 128 129 sc = device_get_softc(dev); 130 mii_sc = &sc->mii_sc; 131 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &dpphy_funcs, 1); 132 133 PHY_RESET(mii_sc); 134 135 rid = 0; 136 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 137 if (sc->irq_res == NULL) 138 goto no_irq; 139 140 error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 141 NULL, dp_intr, sc, &sc->irq_cookie); 142 if (error != 0) { 143 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 144 sc->irq_res = NULL; 145 goto no_irq; 146 } 147 148 /* 149 * ACK and unmask all relevant interrupts. 150 * We have a single register that serves the purpose 151 * of both interrupt status and mask. 152 * Interrupts are cleared on read. 153 */ 154 (void)PHY_READ(mii_sc, DP83822_MISR1); 155 value = DP83822_MISR1_AN_CMPL_EN | 156 DP83822_MISR1_DP_CHG_EN | 157 DP83822_MISR1_SPD_CHG_EN | 158 DP83822_MISR1_LINK_CHG_EN; 159 PHY_WRITE(mii_sc, DP83822_MISR1, value); 160 value = PHY_READ(mii_sc, DP83822_PHYSCR); 161 value |= DP83822_PHYSCR_INT_OE | 162 DP83822_PHYSCR_INT_EN; 163 PHY_WRITE(mii_sc, DP83822_PHYSCR, value); 164 165 no_irq: 166 return (0); 167 } 168 169 static int 170 dp_detach(device_t dev) 171 { 172 struct dp83822_softc *sc; 173 174 sc = device_get_softc(dev); 175 176 bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); 177 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 178 179 return (mii_phy_detach(dev)); 180 } 181 182 static int 183 dp_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 184 { 185 186 switch (cmd) { 187 case MII_POLLSTAT: 188 break; 189 case MII_MEDIACHG: 190 mii_phy_setmedia(sc); 191 break; 192 case MII_TICK: 193 if (mii_phy_tick(sc) == EJUSTRETURN) 194 return (0); 195 196 break; 197 } 198 199 PHY_STATUS(sc); 200 mii_phy_update(sc, cmd); 201 return (0); 202 } 203 204 static device_method_t dp_methods[] = { 205 DEVMETHOD(device_probe, dp_probe), 206 DEVMETHOD(device_attach, dp_attach), 207 DEVMETHOD(device_detach, dp_detach), 208 DEVMETHOD_END 209 }; 210 211 static devclass_t dp_devclass; 212 213 static driver_t dp_driver = { 214 "dp83822phy", 215 dp_methods, 216 sizeof(struct dp83822_softc) 217 }; 218 219 DRIVER_MODULE(dp83822phy, miibus, dp_driver, dp_devclass, 0, 0); 220