blob: ca06b35316d33f92e976522cb2cd563202909b25 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
TsiChung Liew54bdcc92008-10-23 16:27:24 +00002/*
3 * Copyright (C) 2004-2008 Freescale Semiconductor, Inc.
4 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
TsiChung Liew54bdcc92008-10-23 16:27:24 +00005 */
6
7#include <common.h>
8#include <config.h>
9#include <net.h>
10#include <netdev.h>
Simon Glass401d1c42020-10-30 21:38:53 -060011#include <asm/global_data.h>
Simon Glassc05ed002020-05-10 11:40:11 -060012#include <linux/delay.h>
TsiChung Liew54bdcc92008-10-23 16:27:24 +000013
TsiChung Liew54bdcc92008-10-23 16:27:24 +000014#include <asm/fec.h>
TsiChung Liew54bdcc92008-10-23 16:27:24 +000015#include <asm/immap.h>
Simon Glass68a6aa82019-11-14 12:57:31 -070016#include <linux/mii.h>
TsiChung Liew54bdcc92008-10-23 16:27:24 +000017
18DECLARE_GLOBAL_DATA_PTR;
19
Mike Frysingere2a53452011-10-02 10:01:27 +000020#if defined(CONFIG_CMD_NET)
TsiChung Liew54bdcc92008-10-23 16:27:24 +000021#undef MII_DEBUG
22#undef ET_DEBUG
23
24/*extern int fecpin_setclear(struct eth_device *dev, int setclear);*/
25
26#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_CMD_MII)
27#include <miiphy.h>
28
29/* Make MII read/write commands for the FEC. */
30#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \
31 (REG & 0x1f) << 18))
32#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \
33 (REG & 0x1f) << 18) | (VAL & 0xffff))
34
35#ifndef CONFIG_SYS_UNSPEC_PHYID
36# define CONFIG_SYS_UNSPEC_PHYID 0
37#endif
38#ifndef CONFIG_SYS_UNSPEC_STRID
39# define CONFIG_SYS_UNSPEC_STRID 0
40#endif
41
TsiChung Liew54bdcc92008-10-23 16:27:24 +000042typedef struct phy_info_struct {
43 u32 phyid;
44 char *strid;
45} phy_info_t;
46
47phy_info_t phyinfo[] = {
48 {0x0022561B, "AMD79C784VC"}, /* AMD 79C784VC */
49 {0x00406322, "BCM5222"}, /* Broadcom 5222 */
50 {0x02a80150, "Intel82555"}, /* Intel 82555 */
51 {0x0016f870, "LSI80225"}, /* LSI 80225 */
52 {0x0016f880, "LSI80225/B"}, /* LSI 80225/B */
53 {0x78100000, "LXT970"}, /* LXT970 */
54 {0x001378e0, "LXT971"}, /* LXT971 and 972 */
55 {0x00221619, "KS8721BL"}, /* Micrel KS8721BL/SL */
56 {0x00221512, "KSZ8041NL"}, /* Micrel KSZ8041NL */
57 {0x20005CE1, "N83640"}, /* National 83640 */
58 {0x20005C90, "N83848"}, /* National 83848 */
59 {0x20005CA2, "N83849"}, /* National 83849 */
60 {0x01814400, "QS6612"}, /* QS6612 */
61#if defined(CONFIG_SYS_UNSPEC_PHYID) && defined(CONFIG_SYS_UNSPEC_STRID)
62 {CONFIG_SYS_UNSPEC_PHYID, CONFIG_SYS_UNSPEC_STRID},
63#endif
64 {0, 0}
65};
66
67/*
68 * mii_init -- Initialize the MII for MII command without ethernet
69 * This function is a subset of eth_init
70 */
Angelo Durgehello48f885a2019-11-15 23:54:20 +010071void mii_reset(fec_info_t *info)
TsiChung Liew54bdcc92008-10-23 16:27:24 +000072{
73 volatile FEC_T *fecp = (FEC_T *) (info->miibase);
74 int i;
75
76 fecp->ecr = FEC_ECR_RESET;
77
78 for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) {
79 udelay(1);
80 }
81 if (i == FEC_RESET_DELAY)
82 printf("FEC_RESET_DELAY timeout\n");
83}
84
85/* send command to phy using mii, wait for result */
86uint mii_send(uint mii_cmd)
87{
Angelo Durgehello48f885a2019-11-15 23:54:20 +010088#ifdef CONFIG_DM_ETH
89 struct udevice *dev;
90#else
TsiChung Liew54bdcc92008-10-23 16:27:24 +000091 struct eth_device *dev;
Angelo Durgehello48f885a2019-11-15 23:54:20 +010092#endif
93 fec_info_t *info;
94 volatile FEC_T *ep;
TsiChung Liew54bdcc92008-10-23 16:27:24 +000095 uint mii_reply;
96 int j = 0;
97
98 /* retrieve from register structure */
99 dev = eth_get_dev();
Simon Glass0fd3d912020-12-22 19:30:28 -0700100#ifdef CONFIG_DM_ETH
101 info = dev_get_priv(dev);
102#else
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000103 info = dev->priv;
Simon Glass0fd3d912020-12-22 19:30:28 -0700104#endif
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000105
106 ep = (FEC_T *) info->miibase;
107
108 ep->mmfr = mii_cmd; /* command to phy */
109
110 /* wait for mii complete */
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100111 while (!(ep->eir & FEC_EIR_MII) && (j < info->to_loop)) {
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000112 udelay(1);
113 j++;
114 }
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100115 if (j >= info->to_loop) {
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000116 printf("MII not complete\n");
117 return -1;
118 }
119
120 mii_reply = ep->mmfr; /* result from phy */
121 ep->eir = FEC_EIR_MII; /* clear MII complete */
122#ifdef ET_DEBUG
123 printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n",
124 __FILE__, __LINE__, __FUNCTION__, mii_cmd, mii_reply);
125#endif
126
127 return (mii_reply & 0xffff); /* data read from phy */
128}
129#endif /* CONFIG_SYS_DISCOVER_PHY || (CONFIG_MII) */
130
131#if defined(CONFIG_SYS_DISCOVER_PHY)
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100132int mii_discover_phy(fec_info_t *info)
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000133{
134#define MAX_PHY_PASSES 11
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000135 int phyaddr, pass;
136 uint phyno, phytype;
137 int i, found = 0;
138
139 if (info->phyname_init)
140 return info->phy_addr;
141
142 phyaddr = -1; /* didn't find a PHY yet */
143 for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) {
144 if (pass > 1) {
145 /* PHY may need more time to recover from reset.
146 * The LXT970 needs 50ms typical, no maximum is
147 * specified, so wait 10ms before try again.
148 * With 11 passes this gives it 100ms to wake up.
149 */
150 udelay(10000); /* wait 10ms */
151 }
152
153 for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) {
154
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500155 phytype = mii_send(mk_mii_read(phyno, MII_PHYSID1));
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000156#ifdef ET_DEBUG
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100157 printf("PHY type 0x%x pass %d\n", phytype, pass);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000158#endif
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200159 if (phytype == 0xffff)
160 continue;
161 phyaddr = phyno;
162 phytype <<= 16;
163 phytype |=
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500164 mii_send(mk_mii_read(phyno, MII_PHYSID2));
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000165
166#ifdef ET_DEBUG
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200167 printf("PHY @ 0x%x pass %d\n", phyno, pass);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000168#endif
169
Axel Lina62cd292013-07-03 11:24:18 +0800170 for (i = 0; (i < ARRAY_SIZE(phyinfo))
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200171 && (phyinfo[i].phyid != 0); i++) {
172 if (phyinfo[i].phyid == phytype) {
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000173#ifdef ET_DEBUG
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200174 printf("phyid %x - %s\n",
175 phyinfo[i].phyid,
176 phyinfo[i].strid);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000177#endif
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200178 strcpy(info->phy_name, phyinfo[i].strid);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000179 info->phyname_init = 1;
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200180 found = 1;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000181 break;
182 }
183 }
Wolfgang Wegner33f684d2010-04-06 11:13:02 +0200184
185 if (!found) {
186#ifdef ET_DEBUG
187 printf("0x%08x\n", phytype);
188#endif
189 strcpy(info->phy_name, "unknown");
190 info->phyname_init = 1;
191 break;
192 }
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000193 }
194 }
195
196 if (phyaddr < 0)
197 printf("No PHY device found.\n");
198
199 return phyaddr;
200}
201#endif /* CONFIG_SYS_DISCOVER_PHY */
202
203void mii_init(void) __attribute__((weak,alias("__mii_init")));
204
205void __mii_init(void)
206{
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100207#ifdef CONFIG_DM_ETH
208 struct udevice *dev;
209#else
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000210 struct eth_device *dev;
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100211#endif
212 fec_info_t *info;
213 volatile FEC_T *fecp;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000214 int miispd = 0, i = 0;
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500215 u16 status = 0;
216 u16 linkgood = 0;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000217
218 /* retrieve from register structure */
219 dev = eth_get_dev();
Simon Glass0fd3d912020-12-22 19:30:28 -0700220#ifdef CONFIG_DM_ETH
221 info = dev_get_priv(dev);
222#else
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000223 info = dev->priv;
Simon Glass0fd3d912020-12-22 19:30:28 -0700224#endif
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000225
226 fecp = (FEC_T *) info->miibase;
227
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100228 fecpin_setclear(info, 1);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000229
230 mii_reset(info);
231
232 /* We use strictly polling mode only */
233 fecp->eimr = 0;
234
235 /* Clear any pending interrupt */
236 fecp->eir = 0xffffffff;
237
238 /* Set MII speed */
239 miispd = (gd->bus_clk / 1000000) / 5;
240 fecp->mscr = miispd << 1;
241
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100242#ifdef CONFIG_SYS_DISCOVER_PHY
243 info->phy_addr = mii_discover_phy(info);
244#endif
245 if (info->phy_addr == -1)
246 return;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000247
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100248 while (i < info->to_loop) {
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500249 status = 0;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000250 i++;
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500251 /* Read PHY control register */
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500252 miiphy_read(dev->name, info->phy_addr, MII_BMCR, &status);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000253
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500254 /* If phy set to autonegotiate, wait for autonegotiation done,
255 * if phy is not autonegotiating, just wait for link up.
256 */
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500257 if ((status & BMCR_ANENABLE) == BMCR_ANENABLE) {
258 linkgood = (BMSR_ANEGCOMPLETE | BMSR_LSTATUS);
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500259 } else {
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500260 linkgood = BMSR_LSTATUS;
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500261 }
262 /* Read PHY status register */
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500263 miiphy_read(dev->name, info->phy_addr, MII_BMSR, &status);
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500264 if ((status & linkgood) == linkgood)
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000265 break;
266
Richard Retanubun44578be2009-05-26 08:29:29 -0400267 udelay(1);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000268 }
Angelo Durgehello48f885a2019-11-15 23:54:20 +0100269 if (i >= info->to_loop)
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500270 printf("Link UP timeout\n");
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000271
Richard Retanubunc4ff77f2009-01-23 14:42:58 -0500272 /* adapt to the duplex and speed settings of the phy */
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000273 info->dup_spd = miiphy_duplex(dev->name, info->phy_addr) << 16;
274 info->dup_spd |= miiphy_speed(dev->name, info->phy_addr);
275}
276
277/*
278 * Read and write a MII PHY register, routines used by MII Utilities
279 *
280 * FIXME: These routines are expected to return 0 on success, but mii_send
281 * does _not_ return an error code. Maybe 0xFFFF means error, i.e.
282 * no PHY connected...
283 * For now always return 0.
284 * FIXME: These routines only work after calling eth_init() at least once!
285 * Otherwise they hang in mii_send() !!! Sorry!
286 */
287
Joe Hershbergerdfcc4962016-08-08 11:28:39 -0500288int mcffec_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg)
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000289{
290 short rdreg; /* register working value */
291
292#ifdef MII_DEBUG
293 printf("miiphy_read(0x%x) @ 0x%x = ", reg, addr);
294#endif
295 rdreg = mii_send(mk_mii_read(addr, reg));
296
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000297#ifdef MII_DEBUG
Joe Hershbergerdfcc4962016-08-08 11:28:39 -0500298 printf("0x%04x\n", rdreg);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000299#endif
300
Joe Hershbergerdfcc4962016-08-08 11:28:39 -0500301 return rdreg;
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000302}
303
Joe Hershbergerdfcc4962016-08-08 11:28:39 -0500304int mcffec_miiphy_write(struct mii_dev *bus, int addr, int devad, int reg,
305 u16 value)
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000306{
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000307#ifdef MII_DEBUG
Joe Hershbergerdfcc4962016-08-08 11:28:39 -0500308 printf("miiphy_write(0x%x) @ 0x%x = 0x%04x\n", reg, addr, value);
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000309#endif
310
Marek Vasut2b758ca2012-10-03 13:28:47 +0000311 mii_send(mk_mii_write(addr, reg, value));
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000312
TsiChung Liew54bdcc92008-10-23 16:27:24 +0000313 return 0;
314}
315
Mike Frysingere2a53452011-10-02 10:01:27 +0000316#endif /* CONFIG_CMD_NET */