1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * drivers/net/ethernet/ibm/emac/phy.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Driver for PowerPC 4xx on-chip ethernet controller, PHY support.
6*4882a593Smuzhiyun * Borrowed from sungem_phy.c, though I only kept the generic MII
7*4882a593Smuzhiyun * driver for now.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This file should be shared with other drivers or eventually
10*4882a593Smuzhiyun * merged as the "low level" part of miilib
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
13*4882a593Smuzhiyun * <benh@kernel.crashing.org>
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * Based on the arch/ppc version of the driver:
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org)
18*4882a593Smuzhiyun * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun */
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/types.h>
24*4882a593Smuzhiyun #include <linux/netdevice.h>
25*4882a593Smuzhiyun #include <linux/mii.h>
26*4882a593Smuzhiyun #include <linux/ethtool.h>
27*4882a593Smuzhiyun #include <linux/delay.h>
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #include "emac.h"
30*4882a593Smuzhiyun #include "phy.h"
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define phy_read _phy_read
33*4882a593Smuzhiyun #define phy_write _phy_write
34*4882a593Smuzhiyun
_phy_read(struct mii_phy * phy,int reg)35*4882a593Smuzhiyun static inline int _phy_read(struct mii_phy *phy, int reg)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun return phy->mdio_read(phy->dev, phy->address, reg);
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun
_phy_write(struct mii_phy * phy,int reg,int val)40*4882a593Smuzhiyun static inline void _phy_write(struct mii_phy *phy, int reg, int val)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun phy->mdio_write(phy->dev, phy->address, reg, val);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
gpcs_phy_read(struct mii_phy * phy,int reg)45*4882a593Smuzhiyun static inline int gpcs_phy_read(struct mii_phy *phy, int reg)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun return phy->mdio_read(phy->dev, phy->gpcs_address, reg);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
gpcs_phy_write(struct mii_phy * phy,int reg,int val)50*4882a593Smuzhiyun static inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun phy->mdio_write(phy->dev, phy->gpcs_address, reg, val);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
emac_mii_reset_phy(struct mii_phy * phy)55*4882a593Smuzhiyun int emac_mii_reset_phy(struct mii_phy *phy)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun int val;
58*4882a593Smuzhiyun int limit = 10000;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun val = phy_read(phy, MII_BMCR);
61*4882a593Smuzhiyun val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
62*4882a593Smuzhiyun val |= BMCR_RESET;
63*4882a593Smuzhiyun phy_write(phy, MII_BMCR, val);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun udelay(300);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun while (--limit) {
68*4882a593Smuzhiyun val = phy_read(phy, MII_BMCR);
69*4882a593Smuzhiyun if (val >= 0 && (val & BMCR_RESET) == 0)
70*4882a593Smuzhiyun break;
71*4882a593Smuzhiyun udelay(10);
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun if ((val & BMCR_ISOLATE) && limit > 0)
74*4882a593Smuzhiyun phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun return limit <= 0;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
emac_mii_reset_gpcs(struct mii_phy * phy)79*4882a593Smuzhiyun int emac_mii_reset_gpcs(struct mii_phy *phy)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun int val;
82*4882a593Smuzhiyun int limit = 10000;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun val = gpcs_phy_read(phy, MII_BMCR);
85*4882a593Smuzhiyun val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
86*4882a593Smuzhiyun val |= BMCR_RESET;
87*4882a593Smuzhiyun gpcs_phy_write(phy, MII_BMCR, val);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun udelay(300);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun while (--limit) {
92*4882a593Smuzhiyun val = gpcs_phy_read(phy, MII_BMCR);
93*4882a593Smuzhiyun if (val >= 0 && (val & BMCR_RESET) == 0)
94*4882a593Smuzhiyun break;
95*4882a593Smuzhiyun udelay(10);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun if ((val & BMCR_ISOLATE) && limit > 0)
98*4882a593Smuzhiyun gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (limit > 0 && phy->mode == PHY_INTERFACE_MODE_SGMII) {
101*4882a593Smuzhiyun /* Configure GPCS interface to recommended setting for SGMII */
102*4882a593Smuzhiyun gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */
103*4882a593Smuzhiyun gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */
104*4882a593Smuzhiyun gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX */
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun return limit <= 0;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
genmii_setup_aneg(struct mii_phy * phy,u32 advertise)110*4882a593Smuzhiyun static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun int ctl, adv;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun phy->autoneg = AUTONEG_ENABLE;
115*4882a593Smuzhiyun phy->speed = SPEED_10;
116*4882a593Smuzhiyun phy->duplex = DUPLEX_HALF;
117*4882a593Smuzhiyun phy->pause = phy->asym_pause = 0;
118*4882a593Smuzhiyun phy->advertising = advertise;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun ctl = phy_read(phy, MII_BMCR);
121*4882a593Smuzhiyun if (ctl < 0)
122*4882a593Smuzhiyun return ctl;
123*4882a593Smuzhiyun ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* First clear the PHY */
126*4882a593Smuzhiyun phy_write(phy, MII_BMCR, ctl);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /* Setup standard advertise */
129*4882a593Smuzhiyun adv = phy_read(phy, MII_ADVERTISE);
130*4882a593Smuzhiyun if (adv < 0)
131*4882a593Smuzhiyun return adv;
132*4882a593Smuzhiyun adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
133*4882a593Smuzhiyun ADVERTISE_PAUSE_ASYM);
134*4882a593Smuzhiyun if (advertise & ADVERTISED_10baseT_Half)
135*4882a593Smuzhiyun adv |= ADVERTISE_10HALF;
136*4882a593Smuzhiyun if (advertise & ADVERTISED_10baseT_Full)
137*4882a593Smuzhiyun adv |= ADVERTISE_10FULL;
138*4882a593Smuzhiyun if (advertise & ADVERTISED_100baseT_Half)
139*4882a593Smuzhiyun adv |= ADVERTISE_100HALF;
140*4882a593Smuzhiyun if (advertise & ADVERTISED_100baseT_Full)
141*4882a593Smuzhiyun adv |= ADVERTISE_100FULL;
142*4882a593Smuzhiyun if (advertise & ADVERTISED_Pause)
143*4882a593Smuzhiyun adv |= ADVERTISE_PAUSE_CAP;
144*4882a593Smuzhiyun if (advertise & ADVERTISED_Asym_Pause)
145*4882a593Smuzhiyun adv |= ADVERTISE_PAUSE_ASYM;
146*4882a593Smuzhiyun phy_write(phy, MII_ADVERTISE, adv);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (phy->features &
149*4882a593Smuzhiyun (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
150*4882a593Smuzhiyun adv = phy_read(phy, MII_CTRL1000);
151*4882a593Smuzhiyun if (adv < 0)
152*4882a593Smuzhiyun return adv;
153*4882a593Smuzhiyun adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
154*4882a593Smuzhiyun if (advertise & ADVERTISED_1000baseT_Full)
155*4882a593Smuzhiyun adv |= ADVERTISE_1000FULL;
156*4882a593Smuzhiyun if (advertise & ADVERTISED_1000baseT_Half)
157*4882a593Smuzhiyun adv |= ADVERTISE_1000HALF;
158*4882a593Smuzhiyun phy_write(phy, MII_CTRL1000, adv);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun /* Start/Restart aneg */
162*4882a593Smuzhiyun ctl = phy_read(phy, MII_BMCR);
163*4882a593Smuzhiyun ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
164*4882a593Smuzhiyun phy_write(phy, MII_BMCR, ctl);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
genmii_setup_forced(struct mii_phy * phy,int speed,int fd)169*4882a593Smuzhiyun static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun int ctl;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun phy->autoneg = AUTONEG_DISABLE;
174*4882a593Smuzhiyun phy->speed = speed;
175*4882a593Smuzhiyun phy->duplex = fd;
176*4882a593Smuzhiyun phy->pause = phy->asym_pause = 0;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun ctl = phy_read(phy, MII_BMCR);
179*4882a593Smuzhiyun if (ctl < 0)
180*4882a593Smuzhiyun return ctl;
181*4882a593Smuzhiyun ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun /* First clear the PHY */
184*4882a593Smuzhiyun phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /* Select speed & duplex */
187*4882a593Smuzhiyun switch (speed) {
188*4882a593Smuzhiyun case SPEED_10:
189*4882a593Smuzhiyun break;
190*4882a593Smuzhiyun case SPEED_100:
191*4882a593Smuzhiyun ctl |= BMCR_SPEED100;
192*4882a593Smuzhiyun break;
193*4882a593Smuzhiyun case SPEED_1000:
194*4882a593Smuzhiyun ctl |= BMCR_SPEED1000;
195*4882a593Smuzhiyun break;
196*4882a593Smuzhiyun default:
197*4882a593Smuzhiyun return -EINVAL;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun if (fd == DUPLEX_FULL)
200*4882a593Smuzhiyun ctl |= BMCR_FULLDPLX;
201*4882a593Smuzhiyun phy_write(phy, MII_BMCR, ctl);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
genmii_poll_link(struct mii_phy * phy)206*4882a593Smuzhiyun static int genmii_poll_link(struct mii_phy *phy)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun int status;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun /* Clear latched value with dummy read */
211*4882a593Smuzhiyun phy_read(phy, MII_BMSR);
212*4882a593Smuzhiyun status = phy_read(phy, MII_BMSR);
213*4882a593Smuzhiyun if (status < 0 || (status & BMSR_LSTATUS) == 0)
214*4882a593Smuzhiyun return 0;
215*4882a593Smuzhiyun if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE))
216*4882a593Smuzhiyun return 0;
217*4882a593Smuzhiyun return 1;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
genmii_read_link(struct mii_phy * phy)220*4882a593Smuzhiyun static int genmii_read_link(struct mii_phy *phy)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun if (phy->autoneg == AUTONEG_ENABLE) {
223*4882a593Smuzhiyun int glpa = 0;
224*4882a593Smuzhiyun int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE);
225*4882a593Smuzhiyun if (lpa < 0)
226*4882a593Smuzhiyun return lpa;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (phy->features &
229*4882a593Smuzhiyun (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
230*4882a593Smuzhiyun int adv = phy_read(phy, MII_CTRL1000);
231*4882a593Smuzhiyun glpa = phy_read(phy, MII_STAT1000);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun if (glpa < 0 || adv < 0)
234*4882a593Smuzhiyun return adv;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun glpa &= adv << 2;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun phy->speed = SPEED_10;
240*4882a593Smuzhiyun phy->duplex = DUPLEX_HALF;
241*4882a593Smuzhiyun phy->pause = phy->asym_pause = 0;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun if (glpa & (LPA_1000FULL | LPA_1000HALF)) {
244*4882a593Smuzhiyun phy->speed = SPEED_1000;
245*4882a593Smuzhiyun if (glpa & LPA_1000FULL)
246*4882a593Smuzhiyun phy->duplex = DUPLEX_FULL;
247*4882a593Smuzhiyun } else if (lpa & (LPA_100FULL | LPA_100HALF)) {
248*4882a593Smuzhiyun phy->speed = SPEED_100;
249*4882a593Smuzhiyun if (lpa & LPA_100FULL)
250*4882a593Smuzhiyun phy->duplex = DUPLEX_FULL;
251*4882a593Smuzhiyun } else if (lpa & LPA_10FULL)
252*4882a593Smuzhiyun phy->duplex = DUPLEX_FULL;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (phy->duplex == DUPLEX_FULL) {
255*4882a593Smuzhiyun phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
256*4882a593Smuzhiyun phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun } else {
259*4882a593Smuzhiyun int bmcr = phy_read(phy, MII_BMCR);
260*4882a593Smuzhiyun if (bmcr < 0)
261*4882a593Smuzhiyun return bmcr;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun if (bmcr & BMCR_FULLDPLX)
264*4882a593Smuzhiyun phy->duplex = DUPLEX_FULL;
265*4882a593Smuzhiyun else
266*4882a593Smuzhiyun phy->duplex = DUPLEX_HALF;
267*4882a593Smuzhiyun if (bmcr & BMCR_SPEED1000)
268*4882a593Smuzhiyun phy->speed = SPEED_1000;
269*4882a593Smuzhiyun else if (bmcr & BMCR_SPEED100)
270*4882a593Smuzhiyun phy->speed = SPEED_100;
271*4882a593Smuzhiyun else
272*4882a593Smuzhiyun phy->speed = SPEED_10;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun phy->pause = phy->asym_pause = 0;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun return 0;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /* Generic implementation for most 10/100/1000 PHYs */
280*4882a593Smuzhiyun static const struct mii_phy_ops generic_phy_ops = {
281*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
282*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
283*4882a593Smuzhiyun .poll_link = genmii_poll_link,
284*4882a593Smuzhiyun .read_link = genmii_read_link
285*4882a593Smuzhiyun };
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun static struct mii_phy_def genmii_phy_def = {
288*4882a593Smuzhiyun .phy_id = 0x00000000,
289*4882a593Smuzhiyun .phy_id_mask = 0x00000000,
290*4882a593Smuzhiyun .name = "Generic MII",
291*4882a593Smuzhiyun .ops = &generic_phy_ops
292*4882a593Smuzhiyun };
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun /* CIS8201 */
295*4882a593Smuzhiyun #define MII_CIS8201_10BTCSR 0x16
296*4882a593Smuzhiyun #define TENBTCSR_ECHO_DISABLE 0x2000
297*4882a593Smuzhiyun #define MII_CIS8201_EPCR 0x17
298*4882a593Smuzhiyun #define EPCR_MODE_MASK 0x3000
299*4882a593Smuzhiyun #define EPCR_GMII_MODE 0x0000
300*4882a593Smuzhiyun #define EPCR_RGMII_MODE 0x1000
301*4882a593Smuzhiyun #define EPCR_TBI_MODE 0x2000
302*4882a593Smuzhiyun #define EPCR_RTBI_MODE 0x3000
303*4882a593Smuzhiyun #define MII_CIS8201_ACSR 0x1c
304*4882a593Smuzhiyun #define ACSR_PIN_PRIO_SELECT 0x0004
305*4882a593Smuzhiyun
cis8201_init(struct mii_phy * phy)306*4882a593Smuzhiyun static int cis8201_init(struct mii_phy *phy)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun int epcr;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun epcr = phy_read(phy, MII_CIS8201_EPCR);
311*4882a593Smuzhiyun if (epcr < 0)
312*4882a593Smuzhiyun return epcr;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun epcr &= ~EPCR_MODE_MASK;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun switch (phy->mode) {
317*4882a593Smuzhiyun case PHY_INTERFACE_MODE_TBI:
318*4882a593Smuzhiyun epcr |= EPCR_TBI_MODE;
319*4882a593Smuzhiyun break;
320*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RTBI:
321*4882a593Smuzhiyun epcr |= EPCR_RTBI_MODE;
322*4882a593Smuzhiyun break;
323*4882a593Smuzhiyun case PHY_INTERFACE_MODE_GMII:
324*4882a593Smuzhiyun epcr |= EPCR_GMII_MODE;
325*4882a593Smuzhiyun break;
326*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII:
327*4882a593Smuzhiyun default:
328*4882a593Smuzhiyun epcr |= EPCR_RGMII_MODE;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun phy_write(phy, MII_CIS8201_EPCR, epcr);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun /* MII regs override strap pins */
334*4882a593Smuzhiyun phy_write(phy, MII_CIS8201_ACSR,
335*4882a593Smuzhiyun phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT);
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun /* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */
338*4882a593Smuzhiyun phy_write(phy, MII_CIS8201_10BTCSR,
339*4882a593Smuzhiyun phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE);
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun return 0;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun static const struct mii_phy_ops cis8201_phy_ops = {
345*4882a593Smuzhiyun .init = cis8201_init,
346*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
347*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
348*4882a593Smuzhiyun .poll_link = genmii_poll_link,
349*4882a593Smuzhiyun .read_link = genmii_read_link
350*4882a593Smuzhiyun };
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun static struct mii_phy_def cis8201_phy_def = {
353*4882a593Smuzhiyun .phy_id = 0x000fc410,
354*4882a593Smuzhiyun .phy_id_mask = 0x000ffff0,
355*4882a593Smuzhiyun .name = "CIS8201 Gigabit Ethernet",
356*4882a593Smuzhiyun .ops = &cis8201_phy_ops
357*4882a593Smuzhiyun };
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun static struct mii_phy_def bcm5248_phy_def = {
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun .phy_id = 0x0143bc00,
362*4882a593Smuzhiyun .phy_id_mask = 0x0ffffff0,
363*4882a593Smuzhiyun .name = "BCM5248 10/100 SMII Ethernet",
364*4882a593Smuzhiyun .ops = &generic_phy_ops
365*4882a593Smuzhiyun };
366*4882a593Smuzhiyun
m88e1111_init(struct mii_phy * phy)367*4882a593Smuzhiyun static int m88e1111_init(struct mii_phy *phy)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun pr_debug("%s: Marvell 88E1111 Ethernet\n", __func__);
370*4882a593Smuzhiyun phy_write(phy, 0x14, 0x0ce3);
371*4882a593Smuzhiyun phy_write(phy, 0x18, 0x4101);
372*4882a593Smuzhiyun phy_write(phy, 0x09, 0x0e00);
373*4882a593Smuzhiyun phy_write(phy, 0x04, 0x01e1);
374*4882a593Smuzhiyun phy_write(phy, 0x00, 0x9140);
375*4882a593Smuzhiyun phy_write(phy, 0x00, 0x1140);
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun return 0;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun
m88e1112_init(struct mii_phy * phy)380*4882a593Smuzhiyun static int m88e1112_init(struct mii_phy *phy)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun /*
383*4882a593Smuzhiyun * Marvell 88E1112 PHY needs to have the SGMII MAC
384*4882a593Smuzhiyun * interace (page 2) properly configured to
385*4882a593Smuzhiyun * communicate with the 460EX/GT GPCS interface.
386*4882a593Smuzhiyun */
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun u16 reg_short;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__);
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun /* Set access to Page 2 */
393*4882a593Smuzhiyun phy_write(phy, 0x16, 0x0002);
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun phy_write(phy, 0x00, 0x0040); /* 1Gbps */
396*4882a593Smuzhiyun reg_short = (u16)(phy_read(phy, 0x1a));
397*4882a593Smuzhiyun reg_short |= 0x8000; /* bypass Auto-Negotiation */
398*4882a593Smuzhiyun phy_write(phy, 0x1a, reg_short);
399*4882a593Smuzhiyun emac_mii_reset_phy(phy); /* reset MAC interface */
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun /* Reset access to Page 0 */
402*4882a593Smuzhiyun phy_write(phy, 0x16, 0x0000);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun return 0;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
et1011c_init(struct mii_phy * phy)407*4882a593Smuzhiyun static int et1011c_init(struct mii_phy *phy)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun u16 reg_short;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun reg_short = (u16)(phy_read(phy, 0x16));
412*4882a593Smuzhiyun reg_short &= ~(0x7);
413*4882a593Smuzhiyun reg_short |= 0x6; /* RGMII Trace Delay*/
414*4882a593Smuzhiyun phy_write(phy, 0x16, reg_short);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun reg_short = (u16)(phy_read(phy, 0x17));
417*4882a593Smuzhiyun reg_short &= ~(0x40);
418*4882a593Smuzhiyun phy_write(phy, 0x17, reg_short);
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun phy_write(phy, 0x1c, 0x74f0);
421*4882a593Smuzhiyun return 0;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun static const struct mii_phy_ops et1011c_phy_ops = {
425*4882a593Smuzhiyun .init = et1011c_init,
426*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
427*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
428*4882a593Smuzhiyun .poll_link = genmii_poll_link,
429*4882a593Smuzhiyun .read_link = genmii_read_link
430*4882a593Smuzhiyun };
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun static struct mii_phy_def et1011c_phy_def = {
433*4882a593Smuzhiyun .phy_id = 0x0282f000,
434*4882a593Smuzhiyun .phy_id_mask = 0x0fffff00,
435*4882a593Smuzhiyun .name = "ET1011C Gigabit Ethernet",
436*4882a593Smuzhiyun .ops = &et1011c_phy_ops
437*4882a593Smuzhiyun };
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun static const struct mii_phy_ops m88e1111_phy_ops = {
444*4882a593Smuzhiyun .init = m88e1111_init,
445*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
446*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
447*4882a593Smuzhiyun .poll_link = genmii_poll_link,
448*4882a593Smuzhiyun .read_link = genmii_read_link
449*4882a593Smuzhiyun };
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun static struct mii_phy_def m88e1111_phy_def = {
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun .phy_id = 0x01410CC0,
454*4882a593Smuzhiyun .phy_id_mask = 0x0ffffff0,
455*4882a593Smuzhiyun .name = "Marvell 88E1111 Ethernet",
456*4882a593Smuzhiyun .ops = &m88e1111_phy_ops,
457*4882a593Smuzhiyun };
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun static const struct mii_phy_ops m88e1112_phy_ops = {
460*4882a593Smuzhiyun .init = m88e1112_init,
461*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
462*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
463*4882a593Smuzhiyun .poll_link = genmii_poll_link,
464*4882a593Smuzhiyun .read_link = genmii_read_link
465*4882a593Smuzhiyun };
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun static struct mii_phy_def m88e1112_phy_def = {
468*4882a593Smuzhiyun .phy_id = 0x01410C90,
469*4882a593Smuzhiyun .phy_id_mask = 0x0ffffff0,
470*4882a593Smuzhiyun .name = "Marvell 88E1112 Ethernet",
471*4882a593Smuzhiyun .ops = &m88e1112_phy_ops,
472*4882a593Smuzhiyun };
473*4882a593Smuzhiyun
ar8035_init(struct mii_phy * phy)474*4882a593Smuzhiyun static int ar8035_init(struct mii_phy *phy)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */
477*4882a593Smuzhiyun phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */
478*4882a593Smuzhiyun phy_write(phy, 0x1d, 0xb); /* Address hib ctrl */
479*4882a593Smuzhiyun phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun return 0;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun static const struct mii_phy_ops ar8035_phy_ops = {
485*4882a593Smuzhiyun .init = ar8035_init,
486*4882a593Smuzhiyun .setup_aneg = genmii_setup_aneg,
487*4882a593Smuzhiyun .setup_forced = genmii_setup_forced,
488*4882a593Smuzhiyun .poll_link = genmii_poll_link,
489*4882a593Smuzhiyun .read_link = genmii_read_link,
490*4882a593Smuzhiyun };
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun static struct mii_phy_def ar8035_phy_def = {
493*4882a593Smuzhiyun .phy_id = 0x004dd070,
494*4882a593Smuzhiyun .phy_id_mask = 0xfffffff0,
495*4882a593Smuzhiyun .name = "Atheros 8035 Gigabit Ethernet",
496*4882a593Smuzhiyun .ops = &ar8035_phy_ops,
497*4882a593Smuzhiyun };
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun static struct mii_phy_def *mii_phy_table[] = {
500*4882a593Smuzhiyun &et1011c_phy_def,
501*4882a593Smuzhiyun &cis8201_phy_def,
502*4882a593Smuzhiyun &bcm5248_phy_def,
503*4882a593Smuzhiyun &m88e1111_phy_def,
504*4882a593Smuzhiyun &m88e1112_phy_def,
505*4882a593Smuzhiyun &ar8035_phy_def,
506*4882a593Smuzhiyun &genmii_phy_def,
507*4882a593Smuzhiyun NULL
508*4882a593Smuzhiyun };
509*4882a593Smuzhiyun
emac_mii_phy_probe(struct mii_phy * phy,int address)510*4882a593Smuzhiyun int emac_mii_phy_probe(struct mii_phy *phy, int address)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct mii_phy_def *def;
513*4882a593Smuzhiyun int i;
514*4882a593Smuzhiyun u32 id;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun phy->autoneg = AUTONEG_DISABLE;
517*4882a593Smuzhiyun phy->advertising = 0;
518*4882a593Smuzhiyun phy->address = address;
519*4882a593Smuzhiyun phy->speed = SPEED_10;
520*4882a593Smuzhiyun phy->duplex = DUPLEX_HALF;
521*4882a593Smuzhiyun phy->pause = phy->asym_pause = 0;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun /* Take PHY out of isolate mode and reset it. */
524*4882a593Smuzhiyun if (emac_mii_reset_phy(phy))
525*4882a593Smuzhiyun return -ENODEV;
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun /* Read ID and find matching entry */
528*4882a593Smuzhiyun id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
529*4882a593Smuzhiyun for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
530*4882a593Smuzhiyun if ((id & def->phy_id_mask) == def->phy_id)
531*4882a593Smuzhiyun break;
532*4882a593Smuzhiyun /* Should never be NULL (we have a generic entry), but... */
533*4882a593Smuzhiyun if (!def)
534*4882a593Smuzhiyun return -ENODEV;
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun phy->def = def;
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun /* Determine PHY features if needed */
539*4882a593Smuzhiyun phy->features = def->features;
540*4882a593Smuzhiyun if (!phy->features) {
541*4882a593Smuzhiyun u16 bmsr = phy_read(phy, MII_BMSR);
542*4882a593Smuzhiyun if (bmsr & BMSR_ANEGCAPABLE)
543*4882a593Smuzhiyun phy->features |= SUPPORTED_Autoneg;
544*4882a593Smuzhiyun if (bmsr & BMSR_10HALF)
545*4882a593Smuzhiyun phy->features |= SUPPORTED_10baseT_Half;
546*4882a593Smuzhiyun if (bmsr & BMSR_10FULL)
547*4882a593Smuzhiyun phy->features |= SUPPORTED_10baseT_Full;
548*4882a593Smuzhiyun if (bmsr & BMSR_100HALF)
549*4882a593Smuzhiyun phy->features |= SUPPORTED_100baseT_Half;
550*4882a593Smuzhiyun if (bmsr & BMSR_100FULL)
551*4882a593Smuzhiyun phy->features |= SUPPORTED_100baseT_Full;
552*4882a593Smuzhiyun if (bmsr & BMSR_ESTATEN) {
553*4882a593Smuzhiyun u16 esr = phy_read(phy, MII_ESTATUS);
554*4882a593Smuzhiyun if (esr & ESTATUS_1000_TFULL)
555*4882a593Smuzhiyun phy->features |= SUPPORTED_1000baseT_Full;
556*4882a593Smuzhiyun if (esr & ESTATUS_1000_THALF)
557*4882a593Smuzhiyun phy->features |= SUPPORTED_1000baseT_Half;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun phy->features |= SUPPORTED_MII;
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun /* Setup default advertising */
563*4882a593Smuzhiyun phy->advertising = phy->features;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun return 0;
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun MODULE_LICENSE("GPL");
569