blob: 9d429c832f49b7493f8b6e3c6f6f96676dcff1a4 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Dave Liu7737d5c2006-11-03 12:11:15 -06002/*
Kumar Gala2b21ec92011-01-19 03:36:40 -06003 * Copyright (C) 2005,2010-2011 Freescale Semiconductor, Inc.
Dave Liu7737d5c2006-11-03 12:11:15 -06004 *
5 * Author: Shlomi Gridish
6 *
7 * Description: UCC GETH Driver -- PHY handling
Wolfgang Denkdd520bf2006-11-30 18:02:20 +01008 * Driver for UEC on QE
9 * Based on 8260_io/fcc_enet.c
Dave Liu7737d5c2006-11-03 12:11:15 -060010 */
11
Masahiro Yamadab5bf5cb2016-09-21 11:28:53 +090012#include <common.h>
13#include <net.h>
14#include <malloc.h>
Simon Glassc05ed002020-05-10 11:40:11 -060015#include <linux/delay.h>
Masahiro Yamada1221ce42016-09-21 11:28:55 +090016#include <linux/errno.h>
Masahiro Yamadab5bf5cb2016-09-21 11:28:53 +090017#include <linux/immap_qe.h>
18#include <asm/io.h>
Dave Liu7737d5c2006-11-03 12:11:15 -060019#include "uccf.h"
20#include "uec.h"
21#include "uec_phy.h"
22#include "miiphy.h"
Qianyu Gong2459afb2016-02-18 13:01:59 +080023#include <fsl_qe.h>
Andy Fleming865ff852011-04-13 00:37:12 -050024#include <phy.h>
Dave Liu7737d5c2006-11-03 12:11:15 -060025
Heiko Schocher6e31c622020-02-06 09:48:16 +010026#if !defined(CONFIG_DM_ETH)
27
Dave Liu7737d5c2006-11-03 12:11:15 -060028#define ugphy_printk(format, arg...) \
Wolfgang Denkdd520bf2006-11-30 18:02:20 +010029 printf(format "\n", ## arg)
Dave Liu7737d5c2006-11-03 12:11:15 -060030
Wolfgang Denkdd520bf2006-11-30 18:02:20 +010031#define ugphy_dbg(format, arg...) \
Heiko Schocher9bd64442020-05-25 07:27:26 +020032 ugphy_printk(format, ## arg)
Wolfgang Denkdd520bf2006-11-30 18:02:20 +010033#define ugphy_err(format, arg...) \
Heiko Schocher9bd64442020-05-25 07:27:26 +020034 ugphy_printk(format, ## arg)
Wolfgang Denkdd520bf2006-11-30 18:02:20 +010035#define ugphy_info(format, arg...) \
Heiko Schocher9bd64442020-05-25 07:27:26 +020036 ugphy_printk(format, ## arg)
Wolfgang Denkdd520bf2006-11-30 18:02:20 +010037#define ugphy_warn(format, arg...) \
Heiko Schocher9bd64442020-05-25 07:27:26 +020038 ugphy_printk(format, ## arg)
Dave Liu7737d5c2006-11-03 12:11:15 -060039
40#ifdef UEC_VERBOSE_DEBUG
41#define ugphy_vdbg ugphy_dbg
42#else
43#define ugphy_vdbg(ugeth, fmt, args...) do { } while (0)
44#endif /* UEC_VERBOSE_DEBUG */
45
Heiko Schocher9bd64442020-05-25 07:27:26 +020046/*
47 * --------------------------------------------------------------------
Richard Retanubunedf3fe72008-10-23 09:08:18 -040048 * Fixed PHY (PHY-less) support for Ethernet Ports.
49 *
Stefan Roesea47a12b2010-04-15 16:07:28 +020050 * Copied from arch/powerpc/cpu/ppc4xx/4xx_enet.c
Heiko Schocher9bd64442020-05-25 07:27:26 +020051 *--------------------------------------------------------------------
52 *
Richard Retanubun1443cd72009-07-01 14:04:05 -040053 * Some boards do not have a PHY for each ethernet port. These ports are known
54 * as Fixed PHY (or PHY-less) ports. For such ports, set the appropriate
55 * CONFIG_SYS_UECx_PHY_ADDR equal to CONFIG_FIXED_PHY_ADDR (an unused address)
56 * When the drver tries to identify the PHYs, CONFIG_FIXED_PHY will be returned
57 * and the driver will search CONFIG_SYS_FIXED_PHY_PORTS to find what network
58 * speed and duplex should be for the port.
Richard Retanubunedf3fe72008-10-23 09:08:18 -040059 *
Richard Retanubun1443cd72009-07-01 14:04:05 -040060 * Example board header configuration file:
Richard Retanubunedf3fe72008-10-23 09:08:18 -040061 * #define CONFIG_FIXED_PHY 0xFFFFFFFF
Richard Retanubun1443cd72009-07-01 14:04:05 -040062 * #define CONFIG_SYS_FIXED_PHY_ADDR 0x1E (pick an unused phy address)
Richard Retanubunedf3fe72008-10-23 09:08:18 -040063 *
Richard Retanubun1443cd72009-07-01 14:04:05 -040064 * #define CONFIG_SYS_UEC1_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR
65 * #define CONFIG_SYS_UEC2_PHY_ADDR 0x02
66 * #define CONFIG_SYS_UEC3_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR
67 * #define CONFIG_SYS_UEC4_PHY_ADDR 0x04
Richard Retanubunedf3fe72008-10-23 09:08:18 -040068 *
Richard Retanubun1443cd72009-07-01 14:04:05 -040069 * #define CONFIG_SYS_FIXED_PHY_PORT(name,speed,duplex) \
70 * {name, speed, duplex},
Richard Retanubunedf3fe72008-10-23 09:08:18 -040071 *
72 * #define CONFIG_SYS_FIXED_PHY_PORTS \
Kim Phillips78b7a8e2010-07-26 18:34:57 -050073 * CONFIG_SYS_FIXED_PHY_PORT("UEC0",SPEED_100,DUPLEX_FULL) \
74 * CONFIG_SYS_FIXED_PHY_PORT("UEC2",SPEED_100,DUPLEX_HALF)
Richard Retanubunedf3fe72008-10-23 09:08:18 -040075 */
76
77#ifndef CONFIG_FIXED_PHY
78#define CONFIG_FIXED_PHY 0xFFFFFFFF /* Fixed PHY (PHY-less) */
79#endif
80
81#ifndef CONFIG_SYS_FIXED_PHY_PORTS
82#define CONFIG_SYS_FIXED_PHY_PORTS /* default is an empty array */
83#endif
84
85struct fixed_phy_port {
Mike Frysingerf6add132011-11-10 14:11:04 +000086 char name[16]; /* ethernet port name */
Richard Retanubunedf3fe72008-10-23 09:08:18 -040087 unsigned int speed; /* specified speed 10,100 or 1000 */
88 unsigned int duplex; /* specified duplex FULL or HALF */
89};
90
91static const struct fixed_phy_port fixed_phy_port[] = {
92 CONFIG_SYS_FIXED_PHY_PORTS /* defined in board configuration file */
93};
94
Heiko Schocher9bd64442020-05-25 07:27:26 +020095/*
96 * -------------------------------------------------------------------
Richard Retanubun23c34af2009-06-17 16:00:41 -040097 * BitBang MII support for ethernet ports
98 *
99 * Based from MPC8560ADS implementation
Heiko Schocher9bd64442020-05-25 07:27:26 +0200100 *--------------------------------------------------------------------
101 *
Richard Retanubun23c34af2009-06-17 16:00:41 -0400102 * Example board header file to define bitbang ethernet ports:
103 *
104 * #define CONFIG_SYS_BITBANG_PHY_PORT(name) name,
Kim Phillips78b7a8e2010-07-26 18:34:57 -0500105 * #define CONFIG_SYS_BITBANG_PHY_PORTS CONFIG_SYS_BITBANG_PHY_PORT("UEC0")
Heiko Schocher9bd64442020-05-25 07:27:26 +0200106 */
Richard Retanubun23c34af2009-06-17 16:00:41 -0400107#ifndef CONFIG_SYS_BITBANG_PHY_PORTS
108#define CONFIG_SYS_BITBANG_PHY_PORTS /* default is an empty array */
109#endif
110
111#if defined(CONFIG_BITBANGMII)
Heiko Schocher9bd64442020-05-25 07:27:26 +0200112static const char * const bitbang_phy_port[] = {
Richard Retanubun23c34af2009-06-17 16:00:41 -0400113 CONFIG_SYS_BITBANG_PHY_PORTS /* defined in board configuration file */
114};
115#endif /* CONFIG_BITBANGMII */
116
Heiko Schocher9bd64442020-05-25 07:27:26 +0200117static void config_genmii_advert(struct uec_mii_info *mii_info);
118static void genmii_setup_forced(struct uec_mii_info *mii_info);
119static void genmii_restart_aneg(struct uec_mii_info *mii_info);
120static int gbit_config_aneg(struct uec_mii_info *mii_info);
121static int genmii_config_aneg(struct uec_mii_info *mii_info);
122static int genmii_update_link(struct uec_mii_info *mii_info);
123static int genmii_read_status(struct uec_mii_info *mii_info);
124static u16 uec_phy_read(struct uec_mii_info *mii_info, u16 regnum);
125static void uec_phy_write(struct uec_mii_info *mii_info, u16 regnum,
126 u16 val);
Dave Liu7737d5c2006-11-03 12:11:15 -0600127
Heiko Schocher9bd64442020-05-25 07:27:26 +0200128/*
129 * Write value to the PHY for this device to the register at regnum,
130 * waiting until the write is done before it returns. All PHY
131 * configuration has to be done through the TSEC1 MIIM regs
132 */
133void uec_write_phy_reg(struct eth_device *dev, int mii_id, int regnum,
134 int value)
Dave Liu7737d5c2006-11-03 12:11:15 -0600135{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200136 struct uec_priv *ugeth = (struct uec_priv *)dev->priv;
Andy Flemingda9d4612007-08-14 00:14:25 -0500137 uec_mii_t *ug_regs;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200138 enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg)regnum;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100139 u32 tmp_reg;
Dave Liu7737d5c2006-11-03 12:11:15 -0600140
Richard Retanubun23c34af2009-06-17 16:00:41 -0400141#if defined(CONFIG_BITBANGMII)
142 u32 i = 0;
143
144 for (i = 0; i < ARRAY_SIZE(bitbang_phy_port); i++) {
145 if (strncmp(dev->name, bitbang_phy_port[i],
Heiko Schocher9bd64442020-05-25 07:27:26 +0200146 sizeof(dev->name)) == 0) {
Richard Retanubun23c34af2009-06-17 16:00:41 -0400147 (void)bb_miiphy_write(NULL, mii_id, regnum, value);
148 return;
149 }
150 }
151#endif /* CONFIG_BITBANGMII */
152
Andy Flemingda9d4612007-08-14 00:14:25 -0500153 ug_regs = ugeth->uec_mii_regs;
Dave Liu7737d5c2006-11-03 12:11:15 -0600154
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100155 /* Stop the MII management read cycle */
156 out_be32 (&ug_regs->miimcom, 0);
Heiko Schocher9bd64442020-05-25 07:27:26 +0200157 /* Setting up the MII Management Address Register */
158 tmp_reg = ((u32)mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100159 out_be32 (&ug_regs->miimadd, tmp_reg);
Dave Liu7737d5c2006-11-03 12:11:15 -0600160
Heiko Schocher9bd64442020-05-25 07:27:26 +0200161 /* Setting up the MII Management Control Register with the value */
162 out_be32 (&ug_regs->miimcon, (u32)value);
Kim Phillipsee62ed32008-01-15 14:11:00 -0600163 sync();
Dave Liu7737d5c2006-11-03 12:11:15 -0600164
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100165 /* Wait till MII management write is complete */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200166 while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY)
167 ;
Dave Liu7737d5c2006-11-03 12:11:15 -0600168}
169
Heiko Schocher9bd64442020-05-25 07:27:26 +0200170/*
171 * Reads from register regnum in the PHY for device dev,
172 * returning the value. Clears miimcom first. All PHY
173 * configuration has to be done through the TSEC1 MIIM regs
174 */
175int uec_read_phy_reg(struct eth_device *dev, int mii_id, int regnum)
Dave Liu7737d5c2006-11-03 12:11:15 -0600176{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200177 struct uec_priv *ugeth = (struct uec_priv *)dev->priv;
Andy Flemingda9d4612007-08-14 00:14:25 -0500178 uec_mii_t *ug_regs;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200179 enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg)regnum;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100180 u32 tmp_reg;
181 u16 value;
Dave Liu7737d5c2006-11-03 12:11:15 -0600182
Richard Retanubun23c34af2009-06-17 16:00:41 -0400183#if defined(CONFIG_BITBANGMII)
184 u32 i = 0;
185
186 for (i = 0; i < ARRAY_SIZE(bitbang_phy_port); i++) {
187 if (strncmp(dev->name, bitbang_phy_port[i],
Heiko Schocher9bd64442020-05-25 07:27:26 +0200188 sizeof(dev->name)) == 0) {
Richard Retanubun23c34af2009-06-17 16:00:41 -0400189 (void)bb_miiphy_read(NULL, mii_id, regnum, &value);
Heiko Schocher9bd64442020-05-25 07:27:26 +0200190 return value;
Richard Retanubun23c34af2009-06-17 16:00:41 -0400191 }
192 }
193#endif /* CONFIG_BITBANGMII */
194
Andy Flemingda9d4612007-08-14 00:14:25 -0500195 ug_regs = ugeth->uec_mii_regs;
Dave Liu7737d5c2006-11-03 12:11:15 -0600196
Heiko Schocher9bd64442020-05-25 07:27:26 +0200197 /* Setting up the MII Management Address Register */
198 tmp_reg = ((u32)mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100199 out_be32 (&ug_regs->miimadd, tmp_reg);
Dave Liu7737d5c2006-11-03 12:11:15 -0600200
Kim Phillipsee62ed32008-01-15 14:11:00 -0600201 /* clear MII management command cycle */
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100202 out_be32 (&ug_regs->miimcom, 0);
Kim Phillipsee62ed32008-01-15 14:11:00 -0600203 sync();
204
205 /* Perform an MII management read cycle */
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100206 out_be32 (&ug_regs->miimcom, MIIMCOM_READ_CYCLE);
Dave Liu7737d5c2006-11-03 12:11:15 -0600207
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100208 /* Wait till MII management write is complete */
209 while ((in_be32 (&ug_regs->miimind)) &
Heiko Schocher9bd64442020-05-25 07:27:26 +0200210 (MIIMIND_NOT_VALID | MIIMIND_BUSY))
211 ;
Dave Liu7737d5c2006-11-03 12:11:15 -0600212
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100213 /* Read MII management status */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200214 value = (u16)in_be32 (&ug_regs->miimstat);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100215 if (value == 0xffff)
Joakim Tjernlund84a30472008-01-16 09:40:41 +0100216 ugphy_vdbg
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100217 ("read wrong value : mii_id %d,mii_reg %d, base %08x",
Heiko Schocher9bd64442020-05-25 07:27:26 +0200218 mii_id, mii_reg, (u32)&ug_regs->miimcfg);
Dave Liu7737d5c2006-11-03 12:11:15 -0600219
Heiko Schocher9bd64442020-05-25 07:27:26 +0200220 return value;
Dave Liu7737d5c2006-11-03 12:11:15 -0600221}
222
Heiko Schocher9bd64442020-05-25 07:27:26 +0200223void mii_clear_phy_interrupt(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600224{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100225 if (mii_info->phyinfo->ack_interrupt)
Heiko Schocher9bd64442020-05-25 07:27:26 +0200226 mii_info->phyinfo->ack_interrupt(mii_info);
Dave Liu7737d5c2006-11-03 12:11:15 -0600227}
228
Heiko Schocher9bd64442020-05-25 07:27:26 +0200229void mii_configure_phy_interrupt(struct uec_mii_info *mii_info,
230 u32 interrupts)
Dave Liu7737d5c2006-11-03 12:11:15 -0600231{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100232 mii_info->interrupts = interrupts;
233 if (mii_info->phyinfo->config_intr)
Heiko Schocher9bd64442020-05-25 07:27:26 +0200234 mii_info->phyinfo->config_intr(mii_info);
Dave Liu7737d5c2006-11-03 12:11:15 -0600235}
236
237/* Writes MII_ADVERTISE with the appropriate values, after
238 * sanitizing advertise to make sure only supported features
239 * are advertised
240 */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200241static void config_genmii_advert(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600242{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100243 u32 advertise;
244 u16 adv;
Dave Liu7737d5c2006-11-03 12:11:15 -0600245
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100246 /* Only allow advertising what this PHY supports */
247 mii_info->advertising &= mii_info->phyinfo->features;
248 advertise = mii_info->advertising;
Dave Liu7737d5c2006-11-03 12:11:15 -0600249
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100250 /* Setup standard advertisement */
Andy Fleming09c04c22011-03-22 22:49:13 -0500251 adv = uec_phy_read(mii_info, MII_ADVERTISE);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100252 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
253 if (advertise & ADVERTISED_10baseT_Half)
254 adv |= ADVERTISE_10HALF;
255 if (advertise & ADVERTISED_10baseT_Full)
256 adv |= ADVERTISE_10FULL;
257 if (advertise & ADVERTISED_100baseT_Half)
258 adv |= ADVERTISE_100HALF;
259 if (advertise & ADVERTISED_100baseT_Full)
260 adv |= ADVERTISE_100FULL;
Andy Fleming09c04c22011-03-22 22:49:13 -0500261 uec_phy_write(mii_info, MII_ADVERTISE, adv);
Dave Liu7737d5c2006-11-03 12:11:15 -0600262}
263
Heiko Schocher9bd64442020-05-25 07:27:26 +0200264static void genmii_setup_forced(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600265{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100266 u16 ctrl;
267 u32 features = mii_info->phyinfo->features;
Dave Liu7737d5c2006-11-03 12:11:15 -0600268
Andy Fleming09c04c22011-03-22 22:49:13 -0500269 ctrl = uec_phy_read(mii_info, MII_BMCR);
Dave Liu7737d5c2006-11-03 12:11:15 -0600270
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500271 ctrl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 |
272 BMCR_SPEED1000 | BMCR_ANENABLE);
273 ctrl |= BMCR_RESET;
Dave Liu7737d5c2006-11-03 12:11:15 -0600274
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100275 switch (mii_info->speed) {
276 case SPEED_1000:
277 if (features & (SUPPORTED_1000baseT_Half
278 | SUPPORTED_1000baseT_Full)) {
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500279 ctrl |= BMCR_SPEED1000;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100280 break;
281 }
282 mii_info->speed = SPEED_100;
283 case SPEED_100:
284 if (features & (SUPPORTED_100baseT_Half
285 | SUPPORTED_100baseT_Full)) {
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500286 ctrl |= BMCR_SPEED100;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100287 break;
288 }
289 mii_info->speed = SPEED_10;
290 case SPEED_10:
291 if (features & (SUPPORTED_10baseT_Half
292 | SUPPORTED_10baseT_Full))
293 break;
294 default: /* Unsupported speed! */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200295 ugphy_err("%s: Bad speed!", mii_info->dev->name);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100296 break;
297 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600298
Andy Fleming09c04c22011-03-22 22:49:13 -0500299 uec_phy_write(mii_info, MII_BMCR, ctrl);
Dave Liu7737d5c2006-11-03 12:11:15 -0600300}
301
302/* Enable and Restart Autonegotiation */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200303static void genmii_restart_aneg(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600304{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100305 u16 ctl;
Dave Liu7737d5c2006-11-03 12:11:15 -0600306
Andy Fleming09c04c22011-03-22 22:49:13 -0500307 ctl = uec_phy_read(mii_info, MII_BMCR);
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500308 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
Andy Fleming09c04c22011-03-22 22:49:13 -0500309 uec_phy_write(mii_info, MII_BMCR, ctl);
Dave Liu7737d5c2006-11-03 12:11:15 -0600310}
311
Heiko Schocher9bd64442020-05-25 07:27:26 +0200312static int gbit_config_aneg(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600313{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100314 u16 adv;
315 u32 advertise;
Dave Liu7737d5c2006-11-03 12:11:15 -0600316
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100317 if (mii_info->autoneg) {
318 /* Configure the ADVERTISE register */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200319 config_genmii_advert(mii_info);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100320 advertise = mii_info->advertising;
Dave Liu7737d5c2006-11-03 12:11:15 -0600321
Andy Fleming09c04c22011-03-22 22:49:13 -0500322 adv = uec_phy_read(mii_info, MII_CTRL1000);
Kumar Gala2b21ec92011-01-19 03:36:40 -0600323 adv &= ~(ADVERTISE_1000FULL |
324 ADVERTISE_1000HALF);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100325 if (advertise & SUPPORTED_1000baseT_Half)
Kumar Gala2b21ec92011-01-19 03:36:40 -0600326 adv |= ADVERTISE_1000HALF;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100327 if (advertise & SUPPORTED_1000baseT_Full)
Kumar Gala2b21ec92011-01-19 03:36:40 -0600328 adv |= ADVERTISE_1000FULL;
Andy Fleming09c04c22011-03-22 22:49:13 -0500329 uec_phy_write(mii_info, MII_CTRL1000, adv);
Dave Liu7737d5c2006-11-03 12:11:15 -0600330
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100331 /* Start/Restart aneg */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200332 genmii_restart_aneg(mii_info);
333 } else {
334 genmii_setup_forced(mii_info);
335 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600336
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100337 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600338}
339
Heiko Schocher9bd64442020-05-25 07:27:26 +0200340static int marvell_config_aneg(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600341{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200342 /*
343 * The Marvell PHY has an errata which requires
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100344 * that certain registers get written in order
Heiko Schocher9bd64442020-05-25 07:27:26 +0200345 * to restart autonegotiation
346 */
Andy Fleming09c04c22011-03-22 22:49:13 -0500347 uec_phy_write(mii_info, MII_BMCR, BMCR_RESET);
Dave Liu7737d5c2006-11-03 12:11:15 -0600348
Andy Fleming09c04c22011-03-22 22:49:13 -0500349 uec_phy_write(mii_info, 0x1d, 0x1f);
350 uec_phy_write(mii_info, 0x1e, 0x200c);
351 uec_phy_write(mii_info, 0x1d, 0x5);
352 uec_phy_write(mii_info, 0x1e, 0);
353 uec_phy_write(mii_info, 0x1e, 0x100);
Dave Liu7737d5c2006-11-03 12:11:15 -0600354
Heiko Schocher9bd64442020-05-25 07:27:26 +0200355 gbit_config_aneg(mii_info);
Dave Liu7737d5c2006-11-03 12:11:15 -0600356
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100357 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600358}
359
Heiko Schocher9bd64442020-05-25 07:27:26 +0200360static int genmii_config_aneg(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600361{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100362 if (mii_info->autoneg) {
Heiko Schocher9bd64442020-05-25 07:27:26 +0200363 /*
364 * Speed up the common case, if link is already up, speed and
365 * duplex match, skip auto neg as it already matches
366 */
Joakim Tjernlundf29c1812010-08-10 16:36:49 +0200367 if (!genmii_read_status(mii_info) && mii_info->link)
368 if (mii_info->duplex == DUPLEX_FULL &&
369 mii_info->speed == SPEED_100)
370 if (mii_info->advertising &
371 ADVERTISED_100baseT_Full)
372 return 0;
373
Heiko Schocher9bd64442020-05-25 07:27:26 +0200374 config_genmii_advert(mii_info);
375 genmii_restart_aneg(mii_info);
376 } else {
377 genmii_setup_forced(mii_info);
378 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600379
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100380 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600381}
382
Heiko Schocher9bd64442020-05-25 07:27:26 +0200383static int genmii_update_link(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600384{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100385 u16 status;
Dave Liu7737d5c2006-11-03 12:11:15 -0600386
Kim Phillipsee62ed32008-01-15 14:11:00 -0600387 /* Status is read once to clear old link state */
Andy Fleming09c04c22011-03-22 22:49:13 -0500388 uec_phy_read(mii_info, MII_BMSR);
Dave Liu7737d5c2006-11-03 12:11:15 -0600389
Kim Phillipsee62ed32008-01-15 14:11:00 -0600390 /*
391 * Wait if the link is up, and autonegotiation is in progress
392 * (ie - we're capable and it's not done)
393 */
Andy Fleming09c04c22011-03-22 22:49:13 -0500394 status = uec_phy_read(mii_info, MII_BMSR);
Heiko Schocher9bd64442020-05-25 07:27:26 +0200395 if ((status & BMSR_LSTATUS) && (status & BMSR_ANEGCAPABLE) &&
396 !(status & BMSR_ANEGCOMPLETE)) {
Kim Phillipsee62ed32008-01-15 14:11:00 -0600397 int i = 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600398
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500399 while (!(status & BMSR_ANEGCOMPLETE)) {
Kim Phillipsee62ed32008-01-15 14:11:00 -0600400 /*
401 * Timeout reached ?
402 */
403 if (i > UGETH_AN_TIMEOUT) {
404 mii_info->link = 0;
405 return 0;
406 }
407
Kim Phillipsf30b61542008-02-27 16:08:22 -0600408 i++;
Kim Phillipsee62ed32008-01-15 14:11:00 -0600409 udelay(1000); /* 1 ms */
Andy Fleming09c04c22011-03-22 22:49:13 -0500410 status = uec_phy_read(mii_info, MII_BMSR);
Kim Phillipsee62ed32008-01-15 14:11:00 -0600411 }
412 mii_info->link = 1;
Kim Phillipsee62ed32008-01-15 14:11:00 -0600413 } else {
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500414 if (status & BMSR_LSTATUS)
Kim Phillipsee62ed32008-01-15 14:11:00 -0600415 mii_info->link = 1;
416 else
417 mii_info->link = 0;
418 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600419
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100420 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600421}
422
Heiko Schocher9bd64442020-05-25 07:27:26 +0200423static int genmii_read_status(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600424{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100425 u16 status;
426 int err;
Dave Liu7737d5c2006-11-03 12:11:15 -0600427
Heiko Schocher9bd64442020-05-25 07:27:26 +0200428 /* Update the link, but return if there was an error */
429 err = genmii_update_link(mii_info);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100430 if (err)
431 return err;
Dave Liu7737d5c2006-11-03 12:11:15 -0600432
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100433 if (mii_info->autoneg) {
Andy Fleming09c04c22011-03-22 22:49:13 -0500434 status = uec_phy_read(mii_info, MII_STAT1000);
Dave Liu7737d5c2006-11-03 12:11:15 -0600435
Anton Vorontsov91cdaa32008-03-24 20:46:24 +0300436 if (status & (LPA_1000FULL | LPA_1000HALF)) {
437 mii_info->speed = SPEED_1000;
438 if (status & LPA_1000FULL)
439 mii_info->duplex = DUPLEX_FULL;
440 else
441 mii_info->duplex = DUPLEX_HALF;
442 } else {
Andy Fleming09c04c22011-03-22 22:49:13 -0500443 status = uec_phy_read(mii_info, MII_LPA);
Anton Vorontsov91cdaa32008-03-24 20:46:24 +0300444
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500445 if (status & (LPA_10FULL | LPA_100FULL))
Anton Vorontsov91cdaa32008-03-24 20:46:24 +0300446 mii_info->duplex = DUPLEX_FULL;
447 else
448 mii_info->duplex = DUPLEX_HALF;
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500449 if (status & (LPA_100FULL | LPA_100HALF))
Anton Vorontsov91cdaa32008-03-24 20:46:24 +0300450 mii_info->speed = SPEED_100;
451 else
452 mii_info->speed = SPEED_10;
453 }
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100454 mii_info->pause = 0;
455 }
456 /* On non-aneg, we assume what we put in BMCR is the speed,
457 * though magic-aneg shouldn't prevent this case from occurring
458 */
Dave Liu7737d5c2006-11-03 12:11:15 -0600459
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100460 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600461}
462
Anton Vorontsov300615d2008-03-24 20:46:34 +0300463static int bcm_init(struct uec_mii_info *mii_info)
464{
465 struct eth_device *edev = mii_info->dev;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200466 struct uec_priv *uec = edev->priv;
Anton Vorontsov300615d2008-03-24 20:46:34 +0300467
468 gbit_config_aneg(mii_info);
469
Heiko Schocher9bd64442020-05-25 07:27:26 +0200470 if (uec->uec_info->enet_interface_type ==
471 PHY_INTERFACE_MODE_RGMII_RXID &&
472 uec->uec_info->speed == SPEED_1000) {
Anton Vorontsov300615d2008-03-24 20:46:34 +0300473 u16 val;
474 int cnt = 50;
475
476 /* Wait for aneg to complete. */
477 do
Andy Fleming09c04c22011-03-22 22:49:13 -0500478 val = uec_phy_read(mii_info, MII_BMSR);
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500479 while (--cnt && !(val & BMSR_ANEGCOMPLETE));
Anton Vorontsov300615d2008-03-24 20:46:34 +0300480
481 /* Set RDX clk delay. */
Andy Fleming09c04c22011-03-22 22:49:13 -0500482 uec_phy_write(mii_info, 0x18, 0x7 | (7 << 12));
Anton Vorontsov300615d2008-03-24 20:46:34 +0300483
Andy Fleming09c04c22011-03-22 22:49:13 -0500484 val = uec_phy_read(mii_info, 0x18);
Anton Vorontsov300615d2008-03-24 20:46:34 +0300485 /* Set RDX-RXC skew. */
486 val |= (1 << 8);
487 val |= (7 | (7 << 12));
488 /* Write bits 14:0. */
489 val |= (1 << 15);
Andy Fleming09c04c22011-03-22 22:49:13 -0500490 uec_phy_write(mii_info, 0x18, val);
Anton Vorontsov300615d2008-03-24 20:46:34 +0300491 }
492
Heiko Schocher9bd64442020-05-25 07:27:26 +0200493 return 0;
Anton Vorontsov300615d2008-03-24 20:46:34 +0300494}
495
Andy Fleming09c04c22011-03-22 22:49:13 -0500496static int uec_marvell_init(struct uec_mii_info *mii_info)
Haiying Wang41410ee2008-09-24 11:42:12 -0500497{
498 struct eth_device *edev = mii_info->dev;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200499 struct uec_priv *uec = edev->priv;
Andy Fleming865ff852011-04-13 00:37:12 -0500500 phy_interface_t iface = uec->uec_info->enet_interface_type;
Heiko Schocher582c55a2010-01-20 09:04:28 +0100501 int speed = uec->uec_info->speed;
Haiying Wang41410ee2008-09-24 11:42:12 -0500502
Heiko Schocher9bd64442020-05-25 07:27:26 +0200503 if (speed == SPEED_1000 &&
504 (iface == PHY_INTERFACE_MODE_RGMII_ID ||
Andy Fleming865ff852011-04-13 00:37:12 -0500505 iface == PHY_INTERFACE_MODE_RGMII_RXID ||
506 iface == PHY_INTERFACE_MODE_RGMII_TXID)) {
Haiying Wang41410ee2008-09-24 11:42:12 -0500507 int temp;
508
Andy Fleming09c04c22011-03-22 22:49:13 -0500509 temp = uec_phy_read(mii_info, MII_M1111_PHY_EXT_CR);
Andy Fleming865ff852011-04-13 00:37:12 -0500510 if (iface == PHY_INTERFACE_MODE_RGMII_ID) {
Anton Vorontsov6185f802009-09-16 23:21:53 +0400511 temp |= MII_M1111_RX_DELAY | MII_M1111_TX_DELAY;
Andy Fleming865ff852011-04-13 00:37:12 -0500512 } else if (iface == PHY_INTERFACE_MODE_RGMII_RXID) {
Anton Vorontsov6185f802009-09-16 23:21:53 +0400513 temp &= ~MII_M1111_TX_DELAY;
514 temp |= MII_M1111_RX_DELAY;
Andy Fleming865ff852011-04-13 00:37:12 -0500515 } else if (iface == PHY_INTERFACE_MODE_RGMII_TXID) {
Anton Vorontsov6185f802009-09-16 23:21:53 +0400516 temp &= ~MII_M1111_RX_DELAY;
517 temp |= MII_M1111_TX_DELAY;
518 }
Andy Fleming09c04c22011-03-22 22:49:13 -0500519 uec_phy_write(mii_info, MII_M1111_PHY_EXT_CR, temp);
Haiying Wang41410ee2008-09-24 11:42:12 -0500520
Andy Fleming09c04c22011-03-22 22:49:13 -0500521 temp = uec_phy_read(mii_info, MII_M1111_PHY_EXT_SR);
Haiying Wang41410ee2008-09-24 11:42:12 -0500522 temp &= ~MII_M1111_HWCFG_MODE_MASK;
523 temp |= MII_M1111_HWCFG_MODE_RGMII;
Andy Fleming09c04c22011-03-22 22:49:13 -0500524 uec_phy_write(mii_info, MII_M1111_PHY_EXT_SR, temp);
Haiying Wang41410ee2008-09-24 11:42:12 -0500525
Andy Fleming09c04c22011-03-22 22:49:13 -0500526 uec_phy_write(mii_info, MII_BMCR, BMCR_RESET);
Haiying Wang41410ee2008-09-24 11:42:12 -0500527 }
528
529 return 0;
530}
531
Heiko Schocher9bd64442020-05-25 07:27:26 +0200532static int marvell_read_status(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600533{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100534 u16 status;
535 int err;
Dave Liu7737d5c2006-11-03 12:11:15 -0600536
Heiko Schocher9bd64442020-05-25 07:27:26 +0200537 /* Update the link, but return if there was an error */
538 err = genmii_update_link(mii_info);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100539 if (err)
540 return err;
Dave Liu7737d5c2006-11-03 12:11:15 -0600541
Heiko Schocher9bd64442020-05-25 07:27:26 +0200542 /*
543 * If the link is up, read the speed and duplex
544 * If we aren't autonegotiating, assume speeds
545 * are as set
546 */
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100547 if (mii_info->autoneg && mii_info->link) {
548 int speed;
Dave Liu7737d5c2006-11-03 12:11:15 -0600549
Andy Fleming09c04c22011-03-22 22:49:13 -0500550 status = uec_phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
Dave Liu7737d5c2006-11-03 12:11:15 -0600551
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100552 /* Get the duplexity */
553 if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
554 mii_info->duplex = DUPLEX_FULL;
555 else
556 mii_info->duplex = DUPLEX_HALF;
Dave Liu7737d5c2006-11-03 12:11:15 -0600557
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100558 /* Get the speed */
559 speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
560 switch (speed) {
561 case MII_M1011_PHY_SPEC_STATUS_1000:
562 mii_info->speed = SPEED_1000;
563 break;
564 case MII_M1011_PHY_SPEC_STATUS_100:
565 mii_info->speed = SPEED_100;
566 break;
567 default:
568 mii_info->speed = SPEED_10;
569 break;
570 }
571 mii_info->pause = 0;
572 }
573
574 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600575}
576
Heiko Schocher9bd64442020-05-25 07:27:26 +0200577static int marvell_ack_interrupt(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600578{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100579 /* Clear the interrupts by reading the reg */
Andy Fleming09c04c22011-03-22 22:49:13 -0500580 uec_phy_read(mii_info, MII_M1011_IEVENT);
Dave Liu7737d5c2006-11-03 12:11:15 -0600581
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100582 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600583}
584
Heiko Schocher9bd64442020-05-25 07:27:26 +0200585static int marvell_config_intr(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600586{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100587 if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
Andy Fleming09c04c22011-03-22 22:49:13 -0500588 uec_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100589 else
Andy Fleming09c04c22011-03-22 22:49:13 -0500590 uec_phy_write(mii_info, MII_M1011_IMASK,
Heiko Schocher9bd64442020-05-25 07:27:26 +0200591 MII_M1011_IMASK_CLEAR);
Dave Liu7737d5c2006-11-03 12:11:15 -0600592
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100593 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600594}
595
Heiko Schocher9bd64442020-05-25 07:27:26 +0200596static int dm9161_init(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600597{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100598 /* Reset the PHY */
Andy Fleming09c04c22011-03-22 22:49:13 -0500599 uec_phy_write(mii_info, MII_BMCR, uec_phy_read(mii_info, MII_BMCR) |
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500600 BMCR_RESET);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100601 /* PHY and MAC connect */
Andy Fleming09c04c22011-03-22 22:49:13 -0500602 uec_phy_write(mii_info, MII_BMCR, uec_phy_read(mii_info, MII_BMCR) &
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500603 ~BMCR_ISOLATE);
Kim Phillipsee62ed32008-01-15 14:11:00 -0600604
Andy Fleming09c04c22011-03-22 22:49:13 -0500605 uec_phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
Kim Phillipsee62ed32008-01-15 14:11:00 -0600606
Heiko Schocher9bd64442020-05-25 07:27:26 +0200607 config_genmii_advert(mii_info);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100608 /* Start/restart aneg */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200609 genmii_config_aneg(mii_info);
Dave Liu7737d5c2006-11-03 12:11:15 -0600610
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100611 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600612}
613
Heiko Schocher9bd64442020-05-25 07:27:26 +0200614static int dm9161_config_aneg(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600615{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100616 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600617}
618
Heiko Schocher9bd64442020-05-25 07:27:26 +0200619static int dm9161_read_status(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600620{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100621 u16 status;
622 int err;
Dave Liu7737d5c2006-11-03 12:11:15 -0600623
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100624 /* Update the link, but return if there was an error */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200625 err = genmii_update_link(mii_info);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100626 if (err)
627 return err;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200628 /*
629 * If the link is up, read the speed and duplex
630 * If we aren't autonegotiating assume speeds are as set
631 */
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100632 if (mii_info->autoneg && mii_info->link) {
Andy Fleming09c04c22011-03-22 22:49:13 -0500633 status = uec_phy_read(mii_info, MII_DM9161_SCSR);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100634 if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
635 mii_info->speed = SPEED_100;
636 else
637 mii_info->speed = SPEED_10;
Dave Liu7737d5c2006-11-03 12:11:15 -0600638
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100639 if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
640 mii_info->duplex = DUPLEX_FULL;
641 else
642 mii_info->duplex = DUPLEX_HALF;
643 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600644
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100645 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600646}
647
Heiko Schocher9bd64442020-05-25 07:27:26 +0200648static int dm9161_ack_interrupt(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600649{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100650 /* Clear the interrupt by reading the reg */
Andy Fleming09c04c22011-03-22 22:49:13 -0500651 uec_phy_read(mii_info, MII_DM9161_INTR);
Dave Liu7737d5c2006-11-03 12:11:15 -0600652
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100653 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600654}
655
Heiko Schocher9bd64442020-05-25 07:27:26 +0200656static int dm9161_config_intr(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600657{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100658 if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
Andy Fleming09c04c22011-03-22 22:49:13 -0500659 uec_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100660 else
Andy Fleming09c04c22011-03-22 22:49:13 -0500661 uec_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP);
Dave Liu7737d5c2006-11-03 12:11:15 -0600662
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100663 return 0;
Dave Liu7737d5c2006-11-03 12:11:15 -0600664}
665
Heiko Schocher9bd64442020-05-25 07:27:26 +0200666static void dm9161_close(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600667{
668}
669
Heiko Schocher9bd64442020-05-25 07:27:26 +0200670static int fixed_phy_aneg(struct uec_mii_info *mii_info)
Richard Retanubunedf3fe72008-10-23 09:08:18 -0400671{
672 mii_info->autoneg = 0; /* Turn off auto negotiation for fixed phy */
673 return 0;
674}
675
Heiko Schocher9bd64442020-05-25 07:27:26 +0200676static int fixed_phy_read_status(struct uec_mii_info *mii_info)
Richard Retanubunedf3fe72008-10-23 09:08:18 -0400677{
678 int i = 0;
679
680 for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) {
Richard Retanubun1443cd72009-07-01 14:04:05 -0400681 if (strncmp(mii_info->dev->name, fixed_phy_port[i].name,
Heiko Schocher9bd64442020-05-25 07:27:26 +0200682 strlen(mii_info->dev->name)) == 0) {
Richard Retanubunedf3fe72008-10-23 09:08:18 -0400683 mii_info->speed = fixed_phy_port[i].speed;
684 mii_info->duplex = fixed_phy_port[i].duplex;
685 mii_info->link = 1; /* Link is always UP */
686 mii_info->pause = 0;
687 break;
688 }
689 }
690 return 0;
691}
692
Heiko Schocher9bd64442020-05-25 07:27:26 +0200693static int smsc_config_aneg(struct uec_mii_info *mii_info)
Heiko Schocher8b69b562008-11-20 09:57:14 +0100694{
695 return 0;
696}
697
Heiko Schocher9bd64442020-05-25 07:27:26 +0200698static int smsc_read_status(struct uec_mii_info *mii_info)
Heiko Schocher8b69b562008-11-20 09:57:14 +0100699{
700 u16 status;
701 int err;
702
Heiko Schocher9bd64442020-05-25 07:27:26 +0200703 /* Update the link, but return if there was an error */
704 err = genmii_update_link(mii_info);
Heiko Schocher8b69b562008-11-20 09:57:14 +0100705 if (err)
706 return err;
707
Heiko Schocher9bd64442020-05-25 07:27:26 +0200708 /*
709 * If the link is up, read the speed and duplex
710 * If we aren't autonegotiating, assume speeds
711 * are as set
712 */
Heiko Schocher8b69b562008-11-20 09:57:14 +0100713 if (mii_info->autoneg && mii_info->link) {
714 int val;
715
Andy Fleming09c04c22011-03-22 22:49:13 -0500716 status = uec_phy_read(mii_info, 0x1f);
Heiko Schocher8b69b562008-11-20 09:57:14 +0100717 val = (status & 0x1c) >> 2;
718
719 switch (val) {
Heiko Schocher9bd64442020-05-25 07:27:26 +0200720 case 1:
721 mii_info->duplex = DUPLEX_HALF;
722 mii_info->speed = SPEED_10;
723 break;
724 case 5:
725 mii_info->duplex = DUPLEX_FULL;
726 mii_info->speed = SPEED_10;
727 break;
728 case 2:
729 mii_info->duplex = DUPLEX_HALF;
730 mii_info->speed = SPEED_100;
731 break;
732 case 6:
733 mii_info->duplex = DUPLEX_FULL;
734 mii_info->speed = SPEED_100;
735 break;
Heiko Schocher8b69b562008-11-20 09:57:14 +0100736 }
737 mii_info->pause = 0;
738 }
739
740 return 0;
741}
742
Dave Liu7737d5c2006-11-03 12:11:15 -0600743static struct phy_info phy_info_dm9161 = {
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100744 .phy_id = 0x0181b880,
745 .phy_id_mask = 0x0ffffff0,
746 .name = "Davicom DM9161E",
747 .init = dm9161_init,
748 .config_aneg = dm9161_config_aneg,
749 .read_status = dm9161_read_status,
750 .close = dm9161_close,
Dave Liu7737d5c2006-11-03 12:11:15 -0600751};
752
753static struct phy_info phy_info_dm9161a = {
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100754 .phy_id = 0x0181b8a0,
755 .phy_id_mask = 0x0ffffff0,
756 .name = "Davicom DM9161A",
757 .features = MII_BASIC_FEATURES,
758 .init = dm9161_init,
759 .config_aneg = dm9161_config_aneg,
760 .read_status = dm9161_read_status,
761 .ack_interrupt = dm9161_ack_interrupt,
762 .config_intr = dm9161_config_intr,
763 .close = dm9161_close,
Dave Liu7737d5c2006-11-03 12:11:15 -0600764};
765
766static struct phy_info phy_info_marvell = {
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100767 .phy_id = 0x01410c00,
768 .phy_id_mask = 0xffffff00,
769 .name = "Marvell 88E11x1",
770 .features = MII_GBIT_FEATURES,
Andy Fleming09c04c22011-03-22 22:49:13 -0500771 .init = &uec_marvell_init,
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100772 .config_aneg = &marvell_config_aneg,
773 .read_status = &marvell_read_status,
774 .ack_interrupt = &marvell_ack_interrupt,
775 .config_intr = &marvell_config_intr,
Dave Liu7737d5c2006-11-03 12:11:15 -0600776};
777
Anton Vorontsov300615d2008-03-24 20:46:34 +0300778static struct phy_info phy_info_bcm5481 = {
779 .phy_id = 0x0143bca0,
780 .phy_id_mask = 0xffffff0,
781 .name = "Broadcom 5481",
782 .features = MII_GBIT_FEATURES,
783 .read_status = genmii_read_status,
784 .init = bcm_init,
785};
786
Richard Retanubunedf3fe72008-10-23 09:08:18 -0400787static struct phy_info phy_info_fixedphy = {
788 .phy_id = CONFIG_FIXED_PHY,
789 .phy_id_mask = CONFIG_FIXED_PHY,
790 .name = "Fixed PHY",
791 .config_aneg = fixed_phy_aneg,
792 .read_status = fixed_phy_read_status,
793};
794
Heiko Schocher8b69b562008-11-20 09:57:14 +0100795static struct phy_info phy_info_smsclan8700 = {
796 .phy_id = 0x0007c0c0,
797 .phy_id_mask = 0xfffffff0,
798 .name = "SMSC LAN8700",
799 .features = MII_BASIC_FEATURES,
800 .config_aneg = smsc_config_aneg,
801 .read_status = smsc_read_status,
802};
803
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100804static struct phy_info phy_info_genmii = {
805 .phy_id = 0x00000000,
806 .phy_id_mask = 0x00000000,
807 .name = "Generic MII",
808 .features = MII_BASIC_FEATURES,
809 .config_aneg = genmii_config_aneg,
810 .read_status = genmii_read_status,
Dave Liu7737d5c2006-11-03 12:11:15 -0600811};
812
813static struct phy_info *phy_info[] = {
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100814 &phy_info_dm9161,
815 &phy_info_dm9161a,
816 &phy_info_marvell,
Anton Vorontsov300615d2008-03-24 20:46:34 +0300817 &phy_info_bcm5481,
Heiko Schocher8b69b562008-11-20 09:57:14 +0100818 &phy_info_smsclan8700,
Richard Retanubunedf3fe72008-10-23 09:08:18 -0400819 &phy_info_fixedphy,
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100820 &phy_info_genmii,
821 NULL
Dave Liu7737d5c2006-11-03 12:11:15 -0600822};
823
Heiko Schocher9bd64442020-05-25 07:27:26 +0200824static u16 uec_phy_read(struct uec_mii_info *mii_info, u16 regnum)
Dave Liu7737d5c2006-11-03 12:11:15 -0600825{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200826 return mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
Dave Liu7737d5c2006-11-03 12:11:15 -0600827}
828
Heiko Schocher9bd64442020-05-25 07:27:26 +0200829static void uec_phy_write(struct uec_mii_info *mii_info, u16 regnum, u16 val)
Dave Liu7737d5c2006-11-03 12:11:15 -0600830{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200831 mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val);
Dave Liu7737d5c2006-11-03 12:11:15 -0600832}
833
834/* Use the PHY ID registers to determine what type of PHY is attached
835 * to device dev. return a struct phy_info structure describing that PHY
836 */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200837struct phy_info *uec_get_phy_info(struct uec_mii_info *mii_info)
Dave Liu7737d5c2006-11-03 12:11:15 -0600838{
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100839 u16 phy_reg;
840 u32 phy_ID;
841 int i;
Heiko Schocher9bd64442020-05-25 07:27:26 +0200842 struct phy_info *info = NULL;
Dave Liu7737d5c2006-11-03 12:11:15 -0600843
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100844 /* Grab the bits from PHYIR1, and put them in the upper half */
Andy Fleming09c04c22011-03-22 22:49:13 -0500845 phy_reg = uec_phy_read(mii_info, MII_PHYSID1);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100846 phy_ID = (phy_reg & 0xffff) << 16;
Dave Liu7737d5c2006-11-03 12:11:15 -0600847
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100848 /* Grab the bits from PHYIR2, and put them in the lower half */
Andy Fleming09c04c22011-03-22 22:49:13 -0500849 phy_reg = uec_phy_read(mii_info, MII_PHYSID2);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100850 phy_ID |= (phy_reg & 0xffff);
Dave Liu7737d5c2006-11-03 12:11:15 -0600851
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100852 /* loop through all the known PHY types, and find one that */
853 /* matches the ID we read from the PHY. */
854 for (i = 0; phy_info[i]; i++)
855 if (phy_info[i]->phy_id ==
856 (phy_ID & phy_info[i]->phy_id_mask)) {
Heiko Schocher9bd64442020-05-25 07:27:26 +0200857 info = phy_info[i];
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100858 break;
859 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600860
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100861 /* This shouldn't happen, as we have generic PHY support */
Heiko Schocher9bd64442020-05-25 07:27:26 +0200862 if (!info) {
863 ugphy_info("UEC: PHY id %x is not supported!", phy_ID);
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100864 return NULL;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100865 }
Heiko Schocher9bd64442020-05-25 07:27:26 +0200866 ugphy_info("UEC: PHY is %s (%x)", info->name, phy_ID);
Dave Liu7737d5c2006-11-03 12:11:15 -0600867
Heiko Schocher9bd64442020-05-25 07:27:26 +0200868 return info;
Dave Liu7737d5c2006-11-03 12:11:15 -0600869}
870
Andy Fleming865ff852011-04-13 00:37:12 -0500871void marvell_phy_interface_mode(struct eth_device *dev, phy_interface_t type,
Heiko Schocher9bd64442020-05-25 07:27:26 +0200872 int speed)
Dave Liu7737d5c2006-11-03 12:11:15 -0600873{
Heiko Schocher9bd64442020-05-25 07:27:26 +0200874 struct uec_priv *uec = (struct uec_priv *)dev->priv;
Wolfgang Denkdd520bf2006-11-30 18:02:20 +0100875 struct uec_mii_info *mii_info;
Kim Phillipsf655ade2008-02-27 15:06:39 -0600876 u16 status;
Dave Liu7737d5c2006-11-03 12:11:15 -0600877
878 if (!uec->mii_info) {
Heiko Schocher9bd64442020-05-25 07:27:26 +0200879 printf("%s: the PHY not initialized\n", __func__);
Dave Liu7737d5c2006-11-03 12:11:15 -0600880 return;
881 }
882 mii_info = uec->mii_info;
883
Andy Fleming865ff852011-04-13 00:37:12 -0500884 if (type == PHY_INTERFACE_MODE_RGMII) {
885 if (speed == SPEED_100) {
Andy Fleming09c04c22011-03-22 22:49:13 -0500886 uec_phy_write(mii_info, 0x00, 0x9140);
887 uec_phy_write(mii_info, 0x1d, 0x001f);
888 uec_phy_write(mii_info, 0x1e, 0x200c);
889 uec_phy_write(mii_info, 0x1d, 0x0005);
890 uec_phy_write(mii_info, 0x1e, 0x0000);
891 uec_phy_write(mii_info, 0x1e, 0x0100);
892 uec_phy_write(mii_info, 0x09, 0x0e00);
893 uec_phy_write(mii_info, 0x04, 0x01e1);
894 uec_phy_write(mii_info, 0x00, 0x9140);
895 uec_phy_write(mii_info, 0x00, 0x1000);
Simon Glass07e11142020-05-10 11:40:10 -0600896 mdelay(100);
Andy Fleming09c04c22011-03-22 22:49:13 -0500897 uec_phy_write(mii_info, 0x00, 0x2900);
898 uec_phy_write(mii_info, 0x14, 0x0cd2);
899 uec_phy_write(mii_info, 0x00, 0xa100);
900 uec_phy_write(mii_info, 0x09, 0x0000);
901 uec_phy_write(mii_info, 0x1b, 0x800b);
902 uec_phy_write(mii_info, 0x04, 0x05e1);
903 uec_phy_write(mii_info, 0x00, 0xa100);
904 uec_phy_write(mii_info, 0x00, 0x2100);
Simon Glass07e11142020-05-10 11:40:10 -0600905 mdelay(1000);
Andy Fleming865ff852011-04-13 00:37:12 -0500906 } else if (speed == SPEED_10) {
Andy Fleming09c04c22011-03-22 22:49:13 -0500907 uec_phy_write(mii_info, 0x14, 0x8e40);
908 uec_phy_write(mii_info, 0x1b, 0x800b);
909 uec_phy_write(mii_info, 0x14, 0x0c82);
910 uec_phy_write(mii_info, 0x00, 0x8100);
Simon Glass07e11142020-05-10 11:40:10 -0600911 mdelay(1000);
Heiko Schocher582c55a2010-01-20 09:04:28 +0100912 }
Dave Liu7737d5c2006-11-03 12:11:15 -0600913 }
Kim Phillipsf655ade2008-02-27 15:06:39 -0600914
915 /* handle 88e1111 rev.B2 erratum 5.6 */
916 if (mii_info->autoneg) {
Andy Fleming09c04c22011-03-22 22:49:13 -0500917 status = uec_phy_read(mii_info, MII_BMCR);
918 uec_phy_write(mii_info, MII_BMCR, status | BMCR_ANENABLE);
Kim Phillipsf655ade2008-02-27 15:06:39 -0600919 }
920 /* now the B2 will correctly report autoneg completion status */
Dave Liu7737d5c2006-11-03 12:11:15 -0600921}
922
Heiko Schocher9bd64442020-05-25 07:27:26 +0200923void change_phy_interface_mode(struct eth_device *dev,
924 phy_interface_t type, int speed)
Dave Liu7737d5c2006-11-03 12:11:15 -0600925{
926#ifdef CONFIG_PHY_MODE_NEED_CHANGE
Heiko Schocher9bd64442020-05-25 07:27:26 +0200927 marvell_phy_interface_mode(dev, type, speed);
Dave Liu7737d5c2006-11-03 12:11:15 -0600928#endif
929}
Heiko Schocher6e31c622020-02-06 09:48:16 +0100930#endif