xref: /freebsd/sys/dev/mii/dp83822phy.c (revision fdafd315)
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