1*7a9a30c5Sthorpej /* $NetBSD: tqphy.c,v 1.45 2020/03/15 23:04:50 thorpej Exp $ */
257f7d332Ssoren
357f7d332Ssoren /*
449014bf5Sthorpej * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
557f7d332Ssoren * All rights reserved.
657f7d332Ssoren *
757f7d332Ssoren * This code is derived from software contributed to The NetBSD Foundation
857f7d332Ssoren * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
957f7d332Ssoren * NASA Ames Research Center.
1057f7d332Ssoren *
1157f7d332Ssoren * Redistribution and use in source and binary forms, with or without
1257f7d332Ssoren * modification, are permitted provided that the following conditions
1357f7d332Ssoren * are met:
1457f7d332Ssoren * 1. Redistributions of source code must retain the above copyright
1557f7d332Ssoren * notice, this list of conditions and the following disclaimer.
1657f7d332Ssoren * 2. Redistributions in binary form must reproduce the above copyright
1757f7d332Ssoren * notice, this list of conditions and the following disclaimer in the
1857f7d332Ssoren * documentation and/or other materials provided with the distribution.
1957f7d332Ssoren *
2057f7d332Ssoren * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2157f7d332Ssoren * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2257f7d332Ssoren * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2357f7d332Ssoren * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2457f7d332Ssoren * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2557f7d332Ssoren * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2657f7d332Ssoren * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2757f7d332Ssoren * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2857f7d332Ssoren * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2957f7d332Ssoren * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3057f7d332Ssoren * POSSIBILITY OF SUCH DAMAGE.
3157f7d332Ssoren */
3257f7d332Ssoren
3357f7d332Ssoren /*
3457f7d332Ssoren * Copyright (c) 1997 Manuel Bouyer. All rights reserved.
3557f7d332Ssoren *
3657f7d332Ssoren * Redistribution and use in source and binary forms, with or without
3757f7d332Ssoren * modification, are permitted provided that the following conditions
3857f7d332Ssoren * are met:
3957f7d332Ssoren * 1. Redistributions of source code must retain the above copyright
4057f7d332Ssoren * notice, this list of conditions and the following disclaimer.
4157f7d332Ssoren * 2. Redistributions in binary form must reproduce the above copyright
4257f7d332Ssoren * notice, this list of conditions and the following disclaimer in the
4357f7d332Ssoren * documentation and/or other materials provided with the distribution.
4457f7d332Ssoren *
4557f7d332Ssoren * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
4657f7d332Ssoren * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
4757f7d332Ssoren * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
4857f7d332Ssoren * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
4957f7d332Ssoren * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5057f7d332Ssoren * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5157f7d332Ssoren * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5257f7d332Ssoren * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5357f7d332Ssoren * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5457f7d332Ssoren * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5557f7d332Ssoren */
5657f7d332Ssoren
5757f7d332Ssoren /*
5857f7d332Ssoren * TDK TSC78Q2120 PHY driver
5957f7d332Ssoren *
6057f7d332Ssoren * Documentation available at http://www.tsc.tdk.com/lan/78Q2120.pdf .
6157f7d332Ssoren */
6257f7d332Ssoren
638b7bb912Slukem #include <sys/cdefs.h>
64*7a9a30c5Sthorpej __KERNEL_RCSID(0, "$NetBSD: tqphy.c,v 1.45 2020/03/15 23:04:50 thorpej Exp $");
658b7bb912Slukem
6657f7d332Ssoren #include <sys/param.h>
6757f7d332Ssoren #include <sys/systm.h>
6857f7d332Ssoren #include <sys/kernel.h>
6957f7d332Ssoren #include <sys/device.h>
7057f7d332Ssoren #include <sys/socket.h>
7157f7d332Ssoren
7257f7d332Ssoren #include <net/if.h>
7357f7d332Ssoren #include <net/if_media.h>
7457f7d332Ssoren
7557f7d332Ssoren #include <dev/mii/mii.h>
7657f7d332Ssoren #include <dev/mii/miivar.h>
7757f7d332Ssoren #include <dev/mii/miidevs.h>
7857f7d332Ssoren
7957f7d332Ssoren #include <dev/mii/tqphyreg.h>
8057f7d332Ssoren
817db0e577Sxtraeme static int tqphymatch(device_t, cfdata_t, void *);
827db0e577Sxtraeme static void tqphyattach(device_t, device_t, void *);
8357f7d332Ssoren
847db0e577Sxtraeme CFATTACH_DECL_NEW(tqphy, sizeof(struct mii_softc),
85c9b3657cSthorpej tqphymatch, tqphyattach, mii_phy_detach, mii_phy_activate);
8657f7d332Ssoren
871efb3da0Sthorpej static int tqphy_service(struct mii_softc *, struct mii_data *, int);
881efb3da0Sthorpej static void tqphy_status(struct mii_softc *);
8957f7d332Ssoren
901efb3da0Sthorpej static const struct mii_phy_funcs tqphy_funcs = {
9149014bf5Sthorpej tqphy_service, tqphy_status, mii_phy_reset,
9249014bf5Sthorpej };
9349014bf5Sthorpej
941efb3da0Sthorpej static const struct mii_phydesc tqphys[] = {
957b43da1bSchristos MII_PHY_DESC(xxTSC, 78Q2120),
96424f7a1eSthorpej #if 0
977b43da1bSchristos MII_PHY_DESC(xxTSC, 78Q2121),
98424f7a1eSthorpej #endif
997b43da1bSchristos MII_PHY_END,
100424f7a1eSthorpej };
101424f7a1eSthorpej
1021efb3da0Sthorpej static int
tqphymatch(device_t parent,cfdata_t match,void * aux)1037db0e577Sxtraeme tqphymatch(device_t parent, cfdata_t match, void *aux)
10457f7d332Ssoren {
10557f7d332Ssoren struct mii_attach_args *ma = aux;
10657f7d332Ssoren
107aa878fabSmycroft if (mii_phy_match(ma, tqphys) != NULL) {
108aa878fabSmycroft /* The DIAG register is unreliable on early revisions. */
109aa878fabSmycroft if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxTSC_78Q2120 &&
110aa878fabSmycroft MII_REV(ma->mii_id2) <= 3)
1118e65e831Smsaitoh return 0;
1128e65e831Smsaitoh return 10;
113aa878fabSmycroft }
11457f7d332Ssoren
1158e65e831Smsaitoh return 0;
11657f7d332Ssoren }
11757f7d332Ssoren
1181efb3da0Sthorpej static void
tqphyattach(device_t parent,device_t self,void * aux)1197db0e577Sxtraeme tqphyattach(device_t parent, device_t self, void *aux)
12057f7d332Ssoren {
121838ee1e0Sthorpej struct mii_softc *sc = device_private(self);
12257f7d332Ssoren struct mii_attach_args *ma = aux;
12357f7d332Ssoren struct mii_data *mii = ma->mii_data;
124424f7a1eSthorpej const struct mii_phydesc *mpd;
12557f7d332Ssoren
126424f7a1eSthorpej mpd = mii_phy_match(ma, tqphys);
127c31f87a5Sthorpej aprint_naive(": Media interface\n");
128c31f87a5Sthorpej aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
12957f7d332Ssoren
1307db0e577Sxtraeme sc->mii_dev = self;
13157f7d332Ssoren sc->mii_inst = mii->mii_instance;
13257f7d332Ssoren sc->mii_phy = ma->mii_phyno;
13349014bf5Sthorpej sc->mii_funcs = &tqphy_funcs;
13457f7d332Ssoren sc->mii_pdata = mii;
135b0178985Sthorpej sc->mii_flags = ma->mii_flags;
13657f7d332Ssoren
1378e65e831Smsaitoh /* Apparently, we can't do loopback on this PHY. */
1388fc600c3Sthorpej sc->mii_flags |= MIIF_NOLOOP;
13957f7d332Ssoren
140*7a9a30c5Sthorpej mii_lock(mii);
141*7a9a30c5Sthorpej
14249014bf5Sthorpej PHY_RESET(sc);
14357f7d332Ssoren
144a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
145a5cdd4b4Smsaitoh sc->mii_capabilities &= ma->mii_capmask;
146509697f3Smsaitoh
147*7a9a30c5Sthorpej mii_unlock(mii);
148*7a9a30c5Sthorpej
14984dc99fdSthorpej mii_phy_add_media(sc);
15057f7d332Ssoren }
15157f7d332Ssoren
1521efb3da0Sthorpej static int
tqphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)15389893e42Sthorpej tqphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
15457f7d332Ssoren {
15557f7d332Ssoren struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
156a5cdd4b4Smsaitoh uint16_t reg;
15757f7d332Ssoren
158*7a9a30c5Sthorpej KASSERT(mii_locked(mii));
159*7a9a30c5Sthorpej
16057f7d332Ssoren switch (cmd) {
16157f7d332Ssoren case MII_POLLSTAT:
1628e65e831Smsaitoh /* If we're not polling our PHY instance, just return. */
16357f7d332Ssoren if (IFM_INST(ife->ifm_media) != sc->mii_inst)
1648e65e831Smsaitoh return 0;
16557f7d332Ssoren break;
16657f7d332Ssoren
16757f7d332Ssoren case MII_MEDIACHG:
16857f7d332Ssoren /*
16957f7d332Ssoren * If the media indicates a different PHY instance,
17057f7d332Ssoren * isolate ourselves.
17157f7d332Ssoren */
17257f7d332Ssoren if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
173a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, ®);
17457f7d332Ssoren PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
1758e65e831Smsaitoh return 0;
17657f7d332Ssoren }
17757f7d332Ssoren
1788e65e831Smsaitoh /* If the interface is not up, don't do anything. */
17957f7d332Ssoren if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
18057f7d332Ssoren break;
18157f7d332Ssoren
1828fc600c3Sthorpej mii_phy_setmedia(sc);
18357f7d332Ssoren break;
18457f7d332Ssoren
18557f7d332Ssoren case MII_TICK:
1868e65e831Smsaitoh /* If we're not currently selected, just return. */
18757f7d332Ssoren if (IFM_INST(ife->ifm_media) != sc->mii_inst)
1888e65e831Smsaitoh return 0;
18957f7d332Ssoren
190ad61d101Sthorpej if (mii_phy_tick(sc) == EJUSTRETURN)
1918e65e831Smsaitoh return 0;
19257f7d332Ssoren break;
193bca88a28Sthorpej
194bca88a28Sthorpej case MII_DOWN:
195bca88a28Sthorpej mii_phy_down(sc);
1968e65e831Smsaitoh return 0;
19757f7d332Ssoren }
19857f7d332Ssoren
19957f7d332Ssoren /* Update the media status. */
2008923ca0bSthorpej mii_phy_status(sc);
20157f7d332Ssoren
20257f7d332Ssoren /* Callback if something changed. */
203ad61d101Sthorpej mii_phy_update(sc, cmd);
2048e65e831Smsaitoh return 0;
20557f7d332Ssoren }
20657f7d332Ssoren
2071efb3da0Sthorpej static void
tqphy_status(struct mii_softc * sc)20889893e42Sthorpej tqphy_status(struct mii_softc *sc)
20957f7d332Ssoren {
21057f7d332Ssoren struct mii_data *mii = sc->mii_pdata;
2118fc600c3Sthorpej struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
212a5cdd4b4Smsaitoh uint16_t bmsr, bmcr, diag;
21357f7d332Ssoren
214*7a9a30c5Sthorpej KASSERT(mii_locked(mii));
215*7a9a30c5Sthorpej
21657f7d332Ssoren mii->mii_media_status = IFM_AVALID;
21757f7d332Ssoren mii->mii_media_active = IFM_ETHER;
21857f7d332Ssoren
219a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr);
220a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr);
22157f7d332Ssoren if (bmsr & BMSR_LINK)
22257f7d332Ssoren mii->mii_media_status |= IFM_ACTIVE;
22357f7d332Ssoren
224a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &bmcr);
22557f7d332Ssoren if (bmcr & BMCR_ISO) {
22657f7d332Ssoren mii->mii_media_active |= IFM_NONE;
22757f7d332Ssoren mii->mii_media_status = 0;
22857f7d332Ssoren return;
22957f7d332Ssoren }
23057f7d332Ssoren
23157f7d332Ssoren if (bmcr & BMCR_LOOP)
23257f7d332Ssoren mii->mii_media_active |= IFM_LOOP;
23357f7d332Ssoren
23457f7d332Ssoren if (bmcr & BMCR_AUTOEN) {
23557f7d332Ssoren if ((bmsr & BMSR_ACOMP) == 0) {
23657f7d332Ssoren /* Erg, still trying, I guess... */
23757f7d332Ssoren mii->mii_media_active |= IFM_NONE;
23857f7d332Ssoren return;
23957f7d332Ssoren }
240a5cdd4b4Smsaitoh PHY_READ(sc, MII_TQPHY_DIAG, &diag);
24157f7d332Ssoren if (diag & DIAG_RATE)
24257f7d332Ssoren mii->mii_media_active |= IFM_100_TX;
24357f7d332Ssoren else
24457f7d332Ssoren mii->mii_media_active |= IFM_10_T;
245b211437bSmsaitoh
24657f7d332Ssoren if (diag & DIAG_DPLX)
24757f7d332Ssoren mii->mii_media_active |= IFM_FDX;
248b211437bSmsaitoh else
249b211437bSmsaitoh mii->mii_media_active |= IFM_HDX;
25057f7d332Ssoren } else
2518fc600c3Sthorpej mii->mii_media_active = ife->ifm_media;
25257f7d332Ssoren }
253