10c9156faSKornel Duleba /*- 20c9156faSKornel Duleba * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 30c9156faSKornel Duleba * 40c9156faSKornel Duleba * Copyright (c) 2021 Alstom Group. 50c9156faSKornel Duleba * Copyright (c) 2021 Semihalf. 60c9156faSKornel Duleba * All rights reserved. 70c9156faSKornel Duleba * 80c9156faSKornel Duleba * Redistribution and use in source and binary forms, with or without 90c9156faSKornel Duleba * modification, are permitted provided that the following conditions 100c9156faSKornel Duleba * are met: 110c9156faSKornel Duleba * 1. Redistributions of source code must retain the above copyright 120c9156faSKornel Duleba * notice, this list of conditions and the following disclaimer. 130c9156faSKornel Duleba * 2. Redistributions in binary form must reproduce the above copyright 140c9156faSKornel Duleba * notice, this list of conditions and the following disclaimer in the 150c9156faSKornel Duleba * documentation and/or other materials provided with the distribution. 160c9156faSKornel Duleba * 170c9156faSKornel Duleba * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 180c9156faSKornel Duleba * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 190c9156faSKornel Duleba * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 200c9156faSKornel Duleba * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 210c9156faSKornel Duleba * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 220c9156faSKornel Duleba * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 230c9156faSKornel Duleba * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 240c9156faSKornel Duleba * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 250c9156faSKornel Duleba * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 260c9156faSKornel Duleba * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 270c9156faSKornel Duleba * SUCH DAMAGE. 280c9156faSKornel Duleba */ 290c9156faSKornel Duleba 300c9156faSKornel Duleba /* 310c9156faSKornel Duleba * Driver for TI DP83822 Ethernet PHY 320c9156faSKornel Duleba */ 330c9156faSKornel Duleba 340c9156faSKornel Duleba #include <sys/cdefs.h> 350c9156faSKornel Duleba __FBSDID("$FreeBSD$"); 360c9156faSKornel Duleba 370c9156faSKornel Duleba #include <sys/param.h> 380c9156faSKornel Duleba #include <sys/bus.h> 390c9156faSKornel Duleba #include <sys/kernel.h> 400c9156faSKornel Duleba #include <sys/module.h> 410c9156faSKornel Duleba #include <sys/resource.h> 420c9156faSKornel Duleba #include <sys/rman.h> 430c9156faSKornel Duleba #include <sys/socket.h> 440c9156faSKornel Duleba 450c9156faSKornel Duleba #include <machine/resource.h> 460c9156faSKornel Duleba 470c9156faSKornel Duleba #include <net/if.h> 480c9156faSKornel Duleba #include <net/if_media.h> 490c9156faSKornel Duleba 500c9156faSKornel Duleba #include <dev/mii/mii.h> 510c9156faSKornel Duleba #include <dev/mii/miivar.h> 520c9156faSKornel Duleba 530c9156faSKornel Duleba #include "miidevs.h" 540c9156faSKornel Duleba #include "miibus_if.h" 550c9156faSKornel Duleba 560c9156faSKornel Duleba #define BIT(x) (1 << (x)) 570c9156faSKornel Duleba 580c9156faSKornel Duleba #define DP83822_PHYSTS 0x10 590c9156faSKornel Duleba #define DP83822_PHYSTS_LINK_UP BIT(0) 600c9156faSKornel Duleba #define DP83822_PHYSTS_SPEED_100 BIT(1) 610c9156faSKornel Duleba #define DP83822_PHYSTS_FD BIT(2) 620c9156faSKornel Duleba 630c9156faSKornel Duleba #define DP83822_PHYSCR 0x11 640c9156faSKornel Duleba #define DP83822_PHYSCR_INT_OE BIT(0) /* Behaviour of INT pin. */ 650c9156faSKornel Duleba #define DP83822_PHYSCR_INT_EN BIT(1) 660c9156faSKornel Duleba 670c9156faSKornel Duleba #define DP83822_MISR1 0x12 680c9156faSKornel Duleba #define DP83822_MISR1_AN_CMPL_EN BIT(2) 690c9156faSKornel Duleba #define DP83822_MISR1_DP_CHG_EN BIT(3) 700c9156faSKornel Duleba #define DP83822_MISR1_SPD_CHG_EN BIT(4) 710c9156faSKornel Duleba #define DP83822_MISR1_LINK_CHG_EN BIT(5) 720c9156faSKornel Duleba #define DP83822_MISR1_INT_MASK 0xFF 730c9156faSKornel Duleba #define DP83822_MISR1_INT_STS_SHIFT 8 740c9156faSKornel Duleba 750c9156faSKornel Duleba #define DP83822_MISR2 0x13 760c9156faSKornel Duleba #define DP83822_MISR2_AN_ERR_EN BIT(6) 770c9156faSKornel Duleba #define DP83822_MISR2_INT_MASK 0xFF 780c9156faSKornel Duleba #define DP83822_MISR2_INT_STS_SHIFT 8 790c9156faSKornel Duleba 800c9156faSKornel Duleba static int dp_service(struct mii_softc*, struct mii_data*, int); 810c9156faSKornel Duleba static void dp_status(struct mii_softc*); 820c9156faSKornel Duleba 830c9156faSKornel Duleba struct dp83822_softc { 840c9156faSKornel Duleba struct mii_softc mii_sc; 850c9156faSKornel Duleba struct resource *irq_res; 860c9156faSKornel Duleba void *irq_cookie; 870c9156faSKornel Duleba }; 880c9156faSKornel Duleba 890c9156faSKornel Duleba static const struct mii_phydesc dpphys[] = { 900c9156faSKornel Duleba MII_PHY_DESC(xxTI, DP83822) 910c9156faSKornel Duleba }; 920c9156faSKornel Duleba 930c9156faSKornel Duleba static const struct mii_phy_funcs dpphy_funcs = { 940c9156faSKornel Duleba dp_service, 950c9156faSKornel Duleba ukphy_status, 960c9156faSKornel Duleba mii_phy_reset 970c9156faSKornel Duleba }; 980c9156faSKornel Duleba 990c9156faSKornel Duleba static void 1000c9156faSKornel Duleba dp_intr(void *arg) 1010c9156faSKornel Duleba { 1020c9156faSKornel Duleba struct mii_softc *sc = (struct mii_softc *)arg; 1030c9156faSKornel Duleba uint32_t status; 1040c9156faSKornel Duleba 1050c9156faSKornel Duleba status = PHY_READ(sc, DP83822_MISR1); 1060c9156faSKornel Duleba 1070c9156faSKornel Duleba if (!((status >> DP83822_MISR1_INT_STS_SHIFT) & 1080c9156faSKornel Duleba (status & DP83822_MISR1_INT_MASK))) 1090c9156faSKornel Duleba return; 1100c9156faSKornel Duleba 1110c9156faSKornel Duleba PHY_STATUS(sc); 1120c9156faSKornel Duleba mii_phy_update(sc, MII_MEDIACHG); 1130c9156faSKornel Duleba } 1140c9156faSKornel Duleba 1150c9156faSKornel Duleba static int 1160c9156faSKornel Duleba dp_probe(device_t dev) 1170c9156faSKornel Duleba { 1180c9156faSKornel Duleba 1190c9156faSKornel Duleba return (mii_phy_dev_probe(dev, dpphys, BUS_PROBE_DEFAULT)); 1200c9156faSKornel Duleba } 1210c9156faSKornel Duleba 1220c9156faSKornel Duleba static int 1230c9156faSKornel Duleba dp_attach(device_t dev) 1240c9156faSKornel Duleba { 1250c9156faSKornel Duleba struct dp83822_softc *sc; 1260c9156faSKornel Duleba struct mii_softc *mii_sc; 1270c9156faSKornel Duleba uint32_t value; 1280c9156faSKornel Duleba int error, rid; 1290c9156faSKornel Duleba 1300c9156faSKornel Duleba sc = device_get_softc(dev); 1310c9156faSKornel Duleba mii_sc = &sc->mii_sc; 1320c9156faSKornel Duleba mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &dpphy_funcs, 1); 1330c9156faSKornel Duleba 1340c9156faSKornel Duleba PHY_RESET(mii_sc); 1350c9156faSKornel Duleba 1360c9156faSKornel Duleba rid = 0; 1370c9156faSKornel Duleba sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 1380c9156faSKornel Duleba if (sc->irq_res == NULL) 1390c9156faSKornel Duleba goto no_irq; 1400c9156faSKornel Duleba 1410c9156faSKornel Duleba error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 1420c9156faSKornel Duleba NULL, dp_intr, sc, &sc->irq_cookie); 1430c9156faSKornel Duleba if (error != 0) { 1440c9156faSKornel Duleba bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 1450c9156faSKornel Duleba sc->irq_res = NULL; 1460c9156faSKornel Duleba goto no_irq; 1470c9156faSKornel Duleba } 1480c9156faSKornel Duleba 1490c9156faSKornel Duleba /* 1500c9156faSKornel Duleba * ACK and unmask all relevant interrupts. 1510c9156faSKornel Duleba * We have a single register that serves the purpose 1520c9156faSKornel Duleba * of both interrupt status and mask. 1530c9156faSKornel Duleba * Interrupts are cleared on read. 1540c9156faSKornel Duleba */ 1550c9156faSKornel Duleba (void)PHY_READ(mii_sc, DP83822_MISR1); 1560c9156faSKornel Duleba value = DP83822_MISR1_AN_CMPL_EN | 1570c9156faSKornel Duleba DP83822_MISR1_DP_CHG_EN | 1580c9156faSKornel Duleba DP83822_MISR1_SPD_CHG_EN | 1590c9156faSKornel Duleba DP83822_MISR1_LINK_CHG_EN; 1600c9156faSKornel Duleba PHY_WRITE(mii_sc, DP83822_MISR1, value); 1610c9156faSKornel Duleba value = PHY_READ(mii_sc, DP83822_PHYSCR); 1620c9156faSKornel Duleba value |= DP83822_PHYSCR_INT_OE | 1630c9156faSKornel Duleba DP83822_PHYSCR_INT_EN; 1640c9156faSKornel Duleba PHY_WRITE(mii_sc, DP83822_PHYSCR, value); 1650c9156faSKornel Duleba 1660c9156faSKornel Duleba no_irq: 1670c9156faSKornel Duleba return (0); 1680c9156faSKornel Duleba } 1690c9156faSKornel Duleba 1700c9156faSKornel Duleba static int 1710c9156faSKornel Duleba dp_detach(device_t dev) 1720c9156faSKornel Duleba { 1730c9156faSKornel Duleba struct dp83822_softc *sc; 1740c9156faSKornel Duleba 1750c9156faSKornel Duleba sc = device_get_softc(dev); 1760c9156faSKornel Duleba 1770c9156faSKornel Duleba bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); 1780c9156faSKornel Duleba bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 1790c9156faSKornel Duleba 1800c9156faSKornel Duleba return (mii_phy_detach(dev)); 1810c9156faSKornel Duleba } 1820c9156faSKornel Duleba 1830c9156faSKornel Duleba static int 1840c9156faSKornel Duleba dp_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 1850c9156faSKornel Duleba { 1860c9156faSKornel Duleba 1870c9156faSKornel Duleba switch (cmd) { 1880c9156faSKornel Duleba case MII_POLLSTAT: 1890c9156faSKornel Duleba break; 1900c9156faSKornel Duleba case MII_MEDIACHG: 1910c9156faSKornel Duleba mii_phy_setmedia(sc); 1920c9156faSKornel Duleba break; 1930c9156faSKornel Duleba case MII_TICK: 1940c9156faSKornel Duleba if (mii_phy_tick(sc) == EJUSTRETURN) 1950c9156faSKornel Duleba return (0); 1960c9156faSKornel Duleba 1970c9156faSKornel Duleba break; 1980c9156faSKornel Duleba } 1990c9156faSKornel Duleba 2000c9156faSKornel Duleba PHY_STATUS(sc); 2010c9156faSKornel Duleba mii_phy_update(sc, cmd); 2020c9156faSKornel Duleba return (0); 2030c9156faSKornel Duleba } 2040c9156faSKornel Duleba 2050c9156faSKornel Duleba static device_method_t dp_methods[] = { 2060c9156faSKornel Duleba DEVMETHOD(device_probe, dp_probe), 2070c9156faSKornel Duleba DEVMETHOD(device_attach, dp_attach), 2080c9156faSKornel Duleba DEVMETHOD(device_detach, dp_detach), 2090c9156faSKornel Duleba DEVMETHOD_END 2100c9156faSKornel Duleba }; 2110c9156faSKornel Duleba 2120c9156faSKornel Duleba static devclass_t dp_devclass; 2130c9156faSKornel Duleba 2140c9156faSKornel Duleba static driver_t dp_driver = { 2150c9156faSKornel Duleba "dp83822phy", 2160c9156faSKornel Duleba dp_methods, 2170c9156faSKornel Duleba sizeof(struct dp83822_softc) 2180c9156faSKornel Duleba }; 2190c9156faSKornel Duleba 2200c9156faSKornel Duleba DRIVER_MODULE(dp83822phy, miibus, dp_driver, dp_devclass, 0, 0); 221