10c9156faSKornel Duleba /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
350c9156faSKornel Duleba #include <sys/bus.h>
360c9156faSKornel Duleba #include <sys/kernel.h>
370c9156faSKornel Duleba #include <sys/module.h>
380c9156faSKornel Duleba #include <sys/resource.h>
390c9156faSKornel Duleba #include <sys/rman.h>
400c9156faSKornel Duleba #include <sys/socket.h>
410c9156faSKornel Duleba
420c9156faSKornel Duleba #include <machine/resource.h>
430c9156faSKornel Duleba
440c9156faSKornel Duleba #include <net/if.h>
450c9156faSKornel Duleba #include <net/if_media.h>
460c9156faSKornel Duleba
470c9156faSKornel Duleba #include <dev/mii/mii.h>
480c9156faSKornel Duleba #include <dev/mii/miivar.h>
490c9156faSKornel Duleba
500c9156faSKornel Duleba #include "miidevs.h"
510c9156faSKornel Duleba #include "miibus_if.h"
520c9156faSKornel Duleba
530c9156faSKornel Duleba #define BIT(x) (1 << (x))
540c9156faSKornel Duleba
550c9156faSKornel Duleba #define DP83822_PHYSTS 0x10
560c9156faSKornel Duleba #define DP83822_PHYSTS_LINK_UP BIT(0)
570c9156faSKornel Duleba #define DP83822_PHYSTS_SPEED_100 BIT(1)
580c9156faSKornel Duleba #define DP83822_PHYSTS_FD BIT(2)
590c9156faSKornel Duleba
600c9156faSKornel Duleba #define DP83822_PHYSCR 0x11
610c9156faSKornel Duleba #define DP83822_PHYSCR_INT_OE BIT(0) /* Behaviour of INT pin. */
620c9156faSKornel Duleba #define DP83822_PHYSCR_INT_EN BIT(1)
630c9156faSKornel Duleba
640c9156faSKornel Duleba #define DP83822_MISR1 0x12
650c9156faSKornel Duleba #define DP83822_MISR1_AN_CMPL_EN BIT(2)
660c9156faSKornel Duleba #define DP83822_MISR1_DP_CHG_EN BIT(3)
670c9156faSKornel Duleba #define DP83822_MISR1_SPD_CHG_EN BIT(4)
680c9156faSKornel Duleba #define DP83822_MISR1_LINK_CHG_EN BIT(5)
690c9156faSKornel Duleba #define DP83822_MISR1_INT_MASK 0xFF
700c9156faSKornel Duleba #define DP83822_MISR1_INT_STS_SHIFT 8
710c9156faSKornel Duleba
720c9156faSKornel Duleba #define DP83822_MISR2 0x13
730c9156faSKornel Duleba #define DP83822_MISR2_AN_ERR_EN BIT(6)
740c9156faSKornel Duleba #define DP83822_MISR2_INT_MASK 0xFF
750c9156faSKornel Duleba #define DP83822_MISR2_INT_STS_SHIFT 8
760c9156faSKornel Duleba
770c9156faSKornel Duleba static int dp_service(struct mii_softc*, struct mii_data*, int);
780c9156faSKornel Duleba
790c9156faSKornel Duleba struct dp83822_softc {
800c9156faSKornel Duleba struct mii_softc mii_sc;
810c9156faSKornel Duleba struct resource *irq_res;
820c9156faSKornel Duleba void *irq_cookie;
830c9156faSKornel Duleba };
840c9156faSKornel Duleba
850c9156faSKornel Duleba static const struct mii_phydesc dpphys[] = {
86d930ec4fSJessica Clarke MII_PHY_DESC(xxTI, DP83822),
87d930ec4fSJessica Clarke MII_PHY_END
880c9156faSKornel Duleba };
890c9156faSKornel Duleba
900c9156faSKornel Duleba static const struct mii_phy_funcs dpphy_funcs = {
910c9156faSKornel Duleba dp_service,
920c9156faSKornel Duleba ukphy_status,
930c9156faSKornel Duleba mii_phy_reset
940c9156faSKornel Duleba };
950c9156faSKornel Duleba
960c9156faSKornel Duleba static void
dp_intr(void * arg)970c9156faSKornel Duleba dp_intr(void *arg)
980c9156faSKornel Duleba {
990c9156faSKornel Duleba struct mii_softc *sc = (struct mii_softc *)arg;
1000c9156faSKornel Duleba uint32_t status;
1010c9156faSKornel Duleba
1020c9156faSKornel Duleba status = PHY_READ(sc, DP83822_MISR1);
1030c9156faSKornel Duleba
1040c9156faSKornel Duleba if (!((status >> DP83822_MISR1_INT_STS_SHIFT) &
1050c9156faSKornel Duleba (status & DP83822_MISR1_INT_MASK)))
1060c9156faSKornel Duleba return;
1070c9156faSKornel Duleba
1080c9156faSKornel Duleba PHY_STATUS(sc);
1090c9156faSKornel Duleba mii_phy_update(sc, MII_MEDIACHG);
1100c9156faSKornel Duleba }
1110c9156faSKornel Duleba
1120c9156faSKornel Duleba static int
dp_probe(device_t dev)1130c9156faSKornel Duleba dp_probe(device_t dev)
1140c9156faSKornel Duleba {
1150c9156faSKornel Duleba
1160c9156faSKornel Duleba return (mii_phy_dev_probe(dev, dpphys, BUS_PROBE_DEFAULT));
1170c9156faSKornel Duleba }
1180c9156faSKornel Duleba
1190c9156faSKornel Duleba static int
dp_attach(device_t dev)1200c9156faSKornel Duleba dp_attach(device_t dev)
1210c9156faSKornel Duleba {
1220c9156faSKornel Duleba struct dp83822_softc *sc;
1230c9156faSKornel Duleba struct mii_softc *mii_sc;
1240c9156faSKornel Duleba uint32_t value;
1250c9156faSKornel Duleba int error, rid;
1260c9156faSKornel Duleba
1270c9156faSKornel Duleba sc = device_get_softc(dev);
1280c9156faSKornel Duleba mii_sc = &sc->mii_sc;
1290c9156faSKornel Duleba mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &dpphy_funcs, 1);
1300c9156faSKornel Duleba
1310c9156faSKornel Duleba PHY_RESET(mii_sc);
1320c9156faSKornel Duleba
1330c9156faSKornel Duleba rid = 0;
1340c9156faSKornel Duleba sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
1350c9156faSKornel Duleba if (sc->irq_res == NULL)
1360c9156faSKornel Duleba goto no_irq;
1370c9156faSKornel Duleba
1380c9156faSKornel Duleba error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
1390c9156faSKornel Duleba NULL, dp_intr, sc, &sc->irq_cookie);
1400c9156faSKornel Duleba if (error != 0) {
1410c9156faSKornel Duleba bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1420c9156faSKornel Duleba sc->irq_res = NULL;
1430c9156faSKornel Duleba goto no_irq;
1440c9156faSKornel Duleba }
1450c9156faSKornel Duleba
1460c9156faSKornel Duleba /*
1470c9156faSKornel Duleba * ACK and unmask all relevant interrupts.
1480c9156faSKornel Duleba * We have a single register that serves the purpose
1490c9156faSKornel Duleba * of both interrupt status and mask.
1500c9156faSKornel Duleba * Interrupts are cleared on read.
1510c9156faSKornel Duleba */
1520c9156faSKornel Duleba (void)PHY_READ(mii_sc, DP83822_MISR1);
1530c9156faSKornel Duleba value = DP83822_MISR1_AN_CMPL_EN |
1540c9156faSKornel Duleba DP83822_MISR1_DP_CHG_EN |
1550c9156faSKornel Duleba DP83822_MISR1_SPD_CHG_EN |
1560c9156faSKornel Duleba DP83822_MISR1_LINK_CHG_EN;
1570c9156faSKornel Duleba PHY_WRITE(mii_sc, DP83822_MISR1, value);
1580c9156faSKornel Duleba value = PHY_READ(mii_sc, DP83822_PHYSCR);
1590c9156faSKornel Duleba value |= DP83822_PHYSCR_INT_OE |
1600c9156faSKornel Duleba DP83822_PHYSCR_INT_EN;
1610c9156faSKornel Duleba PHY_WRITE(mii_sc, DP83822_PHYSCR, value);
1620c9156faSKornel Duleba
1630c9156faSKornel Duleba no_irq:
1640c9156faSKornel Duleba return (0);
1650c9156faSKornel Duleba }
1660c9156faSKornel Duleba
1670c9156faSKornel Duleba static int
dp_detach(device_t dev)1680c9156faSKornel Duleba dp_detach(device_t dev)
1690c9156faSKornel Duleba {
1700c9156faSKornel Duleba struct dp83822_softc *sc;
1710c9156faSKornel Duleba
1720c9156faSKornel Duleba sc = device_get_softc(dev);
1730c9156faSKornel Duleba
1740c9156faSKornel Duleba bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie);
1750c9156faSKornel Duleba bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1760c9156faSKornel Duleba
1770c9156faSKornel Duleba return (mii_phy_detach(dev));
1780c9156faSKornel Duleba }
1790c9156faSKornel Duleba
1800c9156faSKornel Duleba static int
dp_service(struct mii_softc * sc,struct mii_data * mii,int cmd)1810c9156faSKornel Duleba dp_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
1820c9156faSKornel Duleba {
1830c9156faSKornel Duleba
1840c9156faSKornel Duleba switch (cmd) {
1850c9156faSKornel Duleba case MII_POLLSTAT:
1860c9156faSKornel Duleba break;
1870c9156faSKornel Duleba case MII_MEDIACHG:
1880c9156faSKornel Duleba mii_phy_setmedia(sc);
1890c9156faSKornel Duleba break;
1900c9156faSKornel Duleba case MII_TICK:
1910c9156faSKornel Duleba if (mii_phy_tick(sc) == EJUSTRETURN)
1920c9156faSKornel Duleba return (0);
1930c9156faSKornel Duleba
1940c9156faSKornel Duleba break;
1950c9156faSKornel Duleba }
1960c9156faSKornel Duleba
1970c9156faSKornel Duleba PHY_STATUS(sc);
1980c9156faSKornel Duleba mii_phy_update(sc, cmd);
1990c9156faSKornel Duleba return (0);
2000c9156faSKornel Duleba }
2010c9156faSKornel Duleba
2020c9156faSKornel Duleba static device_method_t dp_methods[] = {
2030c9156faSKornel Duleba DEVMETHOD(device_probe, dp_probe),
2040c9156faSKornel Duleba DEVMETHOD(device_attach, dp_attach),
2050c9156faSKornel Duleba DEVMETHOD(device_detach, dp_detach),
2060c9156faSKornel Duleba DEVMETHOD_END
2070c9156faSKornel Duleba };
2080c9156faSKornel Duleba
2090c9156faSKornel Duleba static driver_t dp_driver = {
2100c9156faSKornel Duleba "dp83822phy",
2110c9156faSKornel Duleba dp_methods,
2120c9156faSKornel Duleba sizeof(struct dp83822_softc)
2130c9156faSKornel Duleba };
2140c9156faSKornel Duleba
215f438c2ffSJohn Baldwin DRIVER_MODULE(dp83822phy, miibus, dp_driver, 0, 0);
216