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