1*7a9a30c5Sthorpej /* $NetBSD: ikphy.c,v 1.19 2020/03/15 23:04:50 thorpej Exp $ */
2154d613fSbouyer
3154d613fSbouyer /*******************************************************************************
4154d613fSbouyer Copyright (c) 2001-2005, Intel Corporation
5154d613fSbouyer All rights reserved.
6154d613fSbouyer
7154d613fSbouyer Redistribution and use in source and binary forms, with or without
8154d613fSbouyer modification, are permitted provided that the following conditions are met:
9154d613fSbouyer
10154d613fSbouyer 1. Redistributions of source code must retain the above copyright notice,
11154d613fSbouyer this list of conditions and the following disclaimer.
12154d613fSbouyer
13154d613fSbouyer 2. Redistributions in binary form must reproduce the above copyright
14154d613fSbouyer notice, this list of conditions and the following disclaimer in the
15154d613fSbouyer documentation and/or other materials provided with the distribution.
16154d613fSbouyer
17154d613fSbouyer 3. Neither the name of the Intel Corporation nor the names of its
18154d613fSbouyer contributors may be used to endorse or promote products derived from
19154d613fSbouyer this software without specific prior written permission.
20154d613fSbouyer
21154d613fSbouyer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22154d613fSbouyer AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23154d613fSbouyer IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24154d613fSbouyer ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25154d613fSbouyer LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26154d613fSbouyer CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27154d613fSbouyer SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28154d613fSbouyer INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29154d613fSbouyer CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30154d613fSbouyer ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31154d613fSbouyer POSSIBILITY OF SUCH DAMAGE.
32154d613fSbouyer *******************************************************************************/
33154d613fSbouyer /*
34154d613fSbouyer * Copyright (c) 2006 Manuel Bouyer. All rights reserved.
35154d613fSbouyer *
36154d613fSbouyer * Redistribution and use in source and binary forms, with or without
37154d613fSbouyer * modification, are permitted provided that the following conditions
38154d613fSbouyer * are met:
39154d613fSbouyer * 1. Redistributions of source code must retain the above copyright
40154d613fSbouyer * notice, this list of conditions and the following disclaimer.
41154d613fSbouyer * 2. Redistributions in binary form must reproduce the above copyright
42154d613fSbouyer * notice, this list of conditions and the following disclaimer in the
43154d613fSbouyer * documentation and/or other materials provided with the distribution.
44154d613fSbouyer *
45154d613fSbouyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46154d613fSbouyer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47154d613fSbouyer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48154d613fSbouyer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49154d613fSbouyer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50154d613fSbouyer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51154d613fSbouyer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52154d613fSbouyer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53154d613fSbouyer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
54154d613fSbouyer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55154d613fSbouyer */
56154d613fSbouyer
57154d613fSbouyer /*
58154d613fSbouyer * driver for Intel's i82563 ethernet 10/100/1000 PHY
59154d613fSbouyer */
60154d613fSbouyer
61154d613fSbouyer #include <sys/cdefs.h>
62*7a9a30c5Sthorpej __KERNEL_RCSID(0, "$NetBSD: ikphy.c,v 1.19 2020/03/15 23:04:50 thorpej Exp $");
63154d613fSbouyer
64154d613fSbouyer #include <sys/param.h>
65154d613fSbouyer #include <sys/systm.h>
66154d613fSbouyer #include <sys/kernel.h>
67154d613fSbouyer #include <sys/device.h>
68154d613fSbouyer #include <sys/socket.h>
69154d613fSbouyer #include <sys/errno.h>
70154d613fSbouyer
71154d613fSbouyer #include <net/if.h>
72154d613fSbouyer #include <net/if_media.h>
73154d613fSbouyer
74154d613fSbouyer #include <dev/mii/mii.h>
75154d613fSbouyer #include <dev/mii/miivar.h>
76154d613fSbouyer #include <dev/mii/miidevs.h>
77154d613fSbouyer
78154d613fSbouyer #include <dev/mii/ikphyreg.h>
79154d613fSbouyer
807db0e577Sxtraeme static int ikphymatch(device_t, cfdata_t, void *);
817db0e577Sxtraeme static void ikphyattach(device_t, device_t, void *);
82154d613fSbouyer
837db0e577Sxtraeme CFATTACH_DECL_NEW(ikphy, sizeof(struct mii_softc),
84154d613fSbouyer ikphymatch, ikphyattach, mii_phy_detach, mii_phy_activate);
85154d613fSbouyer
86154d613fSbouyer static int ikphy_service(struct mii_softc *, struct mii_data *, int);
87154d613fSbouyer static void ikphy_status(struct mii_softc *);
88154d613fSbouyer static void ikphy_setmedia(struct mii_softc *);
89154d613fSbouyer
90154d613fSbouyer static const struct mii_phy_funcs ikphy_funcs = {
91154d613fSbouyer ikphy_service, ikphy_status, mii_phy_reset,
92154d613fSbouyer };
93154d613fSbouyer
94154d613fSbouyer static const struct mii_phydesc ikphys[] = {
957b43da1bSchristos MII_PHY_DESC(xxMARVELL, I82563),
967b43da1bSchristos MII_PHY_END,
97154d613fSbouyer };
98154d613fSbouyer
99154d613fSbouyer static int
ikphymatch(device_t parent,cfdata_t match,void * aux)1007db0e577Sxtraeme ikphymatch(device_t parent, cfdata_t match, void *aux)
101154d613fSbouyer {
102154d613fSbouyer struct mii_attach_args *ma = aux;
103154d613fSbouyer
104154d613fSbouyer if (mii_phy_match(ma, ikphys) != NULL)
10502341723Smsaitoh return 10;
106154d613fSbouyer
10702341723Smsaitoh return 0;
108154d613fSbouyer }
109154d613fSbouyer
110154d613fSbouyer static void
ikphyattach(device_t parent,device_t self,void * aux)1117db0e577Sxtraeme ikphyattach(device_t parent, device_t self, void *aux)
112154d613fSbouyer {
113154d613fSbouyer struct mii_softc *sc = device_private(self);
114154d613fSbouyer struct mii_attach_args *ma = aux;
115154d613fSbouyer struct mii_data *mii = ma->mii_data;
116154d613fSbouyer const struct mii_phydesc *mpd;
117154d613fSbouyer
118154d613fSbouyer mpd = mii_phy_match(ma, ikphys);
119154d613fSbouyer aprint_naive(": Media interface\n");
120154d613fSbouyer aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
121154d613fSbouyer
1227db0e577Sxtraeme sc->mii_dev = self;
123154d613fSbouyer sc->mii_inst = mii->mii_instance;
124154d613fSbouyer sc->mii_phy = ma->mii_phyno;
1258fd2b67fSmsaitoh sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
1268fd2b67fSmsaitoh sc->mii_mpd_model = MII_MODEL(ma->mii_id2);
1278fd2b67fSmsaitoh sc->mii_mpd_rev = MII_REV(ma->mii_id2);
128154d613fSbouyer sc->mii_funcs = &ikphy_funcs;
129154d613fSbouyer sc->mii_pdata = mii;
130154d613fSbouyer sc->mii_flags = ma->mii_flags;
131154d613fSbouyer
132*7a9a30c5Sthorpej mii_lock(mii);
133*7a9a30c5Sthorpej
134154d613fSbouyer PHY_RESET(sc);
135154d613fSbouyer
136a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
137a5cdd4b4Smsaitoh sc->mii_capabilities &= ma->mii_capmask;
138154d613fSbouyer if (sc->mii_capabilities & BMSR_EXTSTAT)
139a5cdd4b4Smsaitoh PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
140509697f3Smsaitoh
141*7a9a30c5Sthorpej mii_unlock(mii);
142*7a9a30c5Sthorpej
143154d613fSbouyer mii_phy_add_media(sc);
144154d613fSbouyer }
145154d613fSbouyer
146154d613fSbouyer static int
ikphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)147154d613fSbouyer ikphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
148154d613fSbouyer {
149154d613fSbouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
150a5cdd4b4Smsaitoh uint16_t reg;
151154d613fSbouyer
152*7a9a30c5Sthorpej KASSERT(mii_locked(mii));
153*7a9a30c5Sthorpej
154154d613fSbouyer switch (cmd) {
155154d613fSbouyer case MII_POLLSTAT:
15602341723Smsaitoh /* If we're not polling our PHY instance, just return. */
157154d613fSbouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst)
15802341723Smsaitoh return 0;
159154d613fSbouyer break;
160154d613fSbouyer
161154d613fSbouyer case MII_MEDIACHG:
162154d613fSbouyer /*
163154d613fSbouyer * If the media indicates a different PHY instance,
164154d613fSbouyer * isolate ourselves.
165154d613fSbouyer */
166154d613fSbouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
167a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, ®);
168154d613fSbouyer PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
16902341723Smsaitoh return 0;
170154d613fSbouyer }
171154d613fSbouyer
17202341723Smsaitoh /* If the interface is not up, don't do anything. */
173154d613fSbouyer if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
174154d613fSbouyer break;
175154d613fSbouyer
176154d613fSbouyer ikphy_setmedia(sc);
177154d613fSbouyer break;
178154d613fSbouyer
179154d613fSbouyer case MII_TICK:
18002341723Smsaitoh /* If we're not currently selected, just return. */
181154d613fSbouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst)
18202341723Smsaitoh return 0;
183154d613fSbouyer
184154d613fSbouyer if (mii_phy_tick(sc) == EJUSTRETURN)
18502341723Smsaitoh return 0;
186154d613fSbouyer break;
187154d613fSbouyer
188154d613fSbouyer case MII_DOWN:
189154d613fSbouyer mii_phy_down(sc);
19002341723Smsaitoh return 0;
191154d613fSbouyer }
192154d613fSbouyer
193154d613fSbouyer /* Update the media status. */
194154d613fSbouyer mii_phy_status(sc);
195154d613fSbouyer
196154d613fSbouyer /* Callback if something changed. */
197154d613fSbouyer mii_phy_update(sc, cmd);
19802341723Smsaitoh return 0;
199154d613fSbouyer }
200154d613fSbouyer
201154d613fSbouyer static void
ikphy_setmedia(struct mii_softc * sc)202154d613fSbouyer ikphy_setmedia(struct mii_softc *sc)
203154d613fSbouyer {
204154d613fSbouyer uint16_t phy_data;
205154d613fSbouyer struct mii_data *mii = sc->mii_pdata;
206154d613fSbouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
207154d613fSbouyer
208*7a9a30c5Sthorpej KASSERT(mii_locked(mii));
209*7a9a30c5Sthorpej
210154d613fSbouyer /* Enable CRS on TX for half-duplex operation. */
211a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL, &phy_data);
212154d613fSbouyer phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
213154d613fSbouyer /* Use 25MHz for both link down and 1000BASE-T for Tx clock */
214154d613fSbouyer phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
215154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
216154d613fSbouyer
217e8be9879Smsaitoh /* Set mdi/mid-x options */
218a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_SPEC_CTRL, &phy_data);
219154d613fSbouyer phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
220154d613fSbouyer if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO)
221154d613fSbouyer phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
222154d613fSbouyer else
223154d613fSbouyer phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
224e8be9879Smsaitoh /* Set polarity correction */
225154d613fSbouyer phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
226154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL, phy_data);
227154d613fSbouyer
228154d613fSbouyer /* SW Reset the PHY so all changes take effect */
229154d613fSbouyer PHY_RESET(sc);
230154d613fSbouyer
231e8be9879Smsaitoh /* For the i80003 */
232a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_SPEC_CTRL_2, &phy_data);
233154d613fSbouyer phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG;
234154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL_2, phy_data);
235154d613fSbouyer
236154d613fSbouyer /* Enable Electrical Idle on the PHY */
237a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_PWR_MGMT_CTRL, &phy_data);
238154d613fSbouyer phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
239154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_PWR_MGMT_CTRL, phy_data);
240154d613fSbouyer
241a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL, &phy_data);
242154d613fSbouyer phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
243154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, phy_data);
244154d613fSbouyer
245154d613fSbouyer /*
246154d613fSbouyer * Workaround: Disable padding in Kumeran interface in the MAC
247154d613fSbouyer * and in the PHY to avoid CRC errors.
248154d613fSbouyer */
249a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_INBAND_CTRL, &phy_data);
250154d613fSbouyer phy_data |= GG82563_ICR_DIS_PADDING;
251154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_INBAND_CTRL, phy_data);
252154d613fSbouyer
253154d613fSbouyer mii_phy_setmedia(sc);
254154d613fSbouyer if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
255154d613fSbouyer /*
25602341723Smsaitoh * When not in auto mode, we need to restart nego
257154d613fSbouyer * anyway, or a switch from a fixed mode to another
258154d613fSbouyer * fixed mode may not be seen by the switch.
259154d613fSbouyer */
260a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &phy_data);
261a5cdd4b4Smsaitoh PHY_WRITE(sc, MII_BMCR, phy_data | BMCR_STARTNEG);
262154d613fSbouyer }
263a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL, &phy_data);
264154d613fSbouyer phy_data &= ~GG82563_MSCR_TX_CLK_MASK;
265154d613fSbouyer switch (IFM_SUBTYPE(ife->ifm_media)) {
266154d613fSbouyer case IFM_10_T:
267154d613fSbouyer phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ;
268154d613fSbouyer break;
269154d613fSbouyer case IFM_100_TX:
270154d613fSbouyer phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ;
271154d613fSbouyer break;
272154d613fSbouyer case IFM_1000_T:
273154d613fSbouyer phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
274154d613fSbouyer break;
275154d613fSbouyer }
276154d613fSbouyer phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
277154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
278154d613fSbouyer }
279154d613fSbouyer
280154d613fSbouyer static void
ikphy_status(struct mii_softc * sc)281154d613fSbouyer ikphy_status(struct mii_softc *sc)
282154d613fSbouyer {
283154d613fSbouyer struct mii_data *mii = sc->mii_pdata;
284154d613fSbouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
285a5cdd4b4Smsaitoh uint16_t pssr, bmcr, gtsr, kmrn;
286154d613fSbouyer
287*7a9a30c5Sthorpej KASSERT(mii_locked(mii));
288*7a9a30c5Sthorpej
289154d613fSbouyer mii->mii_media_status = IFM_AVALID;
290154d613fSbouyer mii->mii_media_active = IFM_ETHER;
291154d613fSbouyer
292a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_SPEC_STATUS, &pssr);
293154d613fSbouyer
294154d613fSbouyer if (pssr & GG82563_PSSR_LINK)
295154d613fSbouyer mii->mii_media_status |= IFM_ACTIVE;
296154d613fSbouyer
297a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &bmcr);
298154d613fSbouyer if (bmcr & BMCR_ISO) {
299154d613fSbouyer mii->mii_media_active |= IFM_NONE;
300154d613fSbouyer mii->mii_media_status = 0;
301154d613fSbouyer return;
302154d613fSbouyer }
303154d613fSbouyer
304154d613fSbouyer if (bmcr & BMCR_LOOP)
305154d613fSbouyer mii->mii_media_active |= IFM_LOOP;
306154d613fSbouyer
307154d613fSbouyer if (bmcr & BMCR_AUTOEN) {
308154d613fSbouyer /*
309913e68ceSmsaitoh * The media status bits are only valid if autonegotiation
310154d613fSbouyer * has completed (or it's disabled).
311154d613fSbouyer */
312154d613fSbouyer if ((pssr & GG82563_PSSR_SPEED_DUPLEX_RESOLVED) == 0) {
313154d613fSbouyer /* Erg, still trying, I guess... */
314154d613fSbouyer mii->mii_media_active |= IFM_NONE;
315154d613fSbouyer return;
316154d613fSbouyer }
317154d613fSbouyer
318154d613fSbouyer switch (pssr & GG82563_PSSR_SPEED_MASK) {
319154d613fSbouyer case GG82563_PSSR_SPEED_1000MBPS:
320154d613fSbouyer mii->mii_media_active |= IFM_1000_T;
321a5cdd4b4Smsaitoh PHY_READ(sc, MII_100T2SR, >sr);
322154d613fSbouyer if (gtsr & GTSR_MS_RES)
323154d613fSbouyer mii->mii_media_active |= IFM_ETH_MASTER;
324154d613fSbouyer break;
325154d613fSbouyer
326154d613fSbouyer case GG82563_PSSR_SPEED_100MBPS:
327154d613fSbouyer mii->mii_media_active |= IFM_100_TX;
328154d613fSbouyer break;
329154d613fSbouyer
330154d613fSbouyer case GG82563_PSSR_SPEED_10MBPS:
331154d613fSbouyer mii->mii_media_active |= IFM_10_T;
332154d613fSbouyer break;
333154d613fSbouyer
334154d613fSbouyer default:
335154d613fSbouyer mii->mii_media_active |= IFM_NONE;
336154d613fSbouyer mii->mii_media_status = 0;
337154d613fSbouyer return;
338154d613fSbouyer }
339154d613fSbouyer
340154d613fSbouyer if (pssr & GG82563_PSSR_DUPLEX)
341154d613fSbouyer mii->mii_media_active |=
342154d613fSbouyer IFM_FDX | mii_phy_flowstatus(sc);
343b211437bSmsaitoh else
344b211437bSmsaitoh mii->mii_media_active |= IFM_HDX;
345154d613fSbouyer } else
346154d613fSbouyer mii->mii_media_active = ife->ifm_media;
347a5cdd4b4Smsaitoh PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL, &kmrn);
348154d613fSbouyer if (mii->mii_media_active & IFM_FDX)
349154d613fSbouyer kmrn &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
350154d613fSbouyer else
351154d613fSbouyer kmrn |= GG82563_KMCR_PASS_FALSE_CARRIER;
352154d613fSbouyer PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, kmrn);
353154d613fSbouyer }
354