162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/ethernet/ibm/emac/phy.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, PHY support.
662306a36Sopenharmony_ci * Borrowed from sungem_phy.c, though I only kept the generic MII
762306a36Sopenharmony_ci * driver for now.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file should be shared with other drivers or eventually
1062306a36Sopenharmony_ci * merged as the "low level" part of miilib
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
1362306a36Sopenharmony_ci *                <benh@kernel.crashing.org>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Based on the arch/ppc version of the driver:
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org)
1862306a36Sopenharmony_ci * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/mii.h>
2662306a36Sopenharmony_ci#include <linux/ethtool.h>
2762306a36Sopenharmony_ci#include <linux/delay.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "emac.h"
3062306a36Sopenharmony_ci#include "phy.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define phy_read _phy_read
3362306a36Sopenharmony_ci#define phy_write _phy_write
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic inline int _phy_read(struct mii_phy *phy, int reg)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	return phy->mdio_read(phy->dev, phy->address, reg);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline void _phy_write(struct mii_phy *phy, int reg, int val)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	phy->mdio_write(phy->dev, phy->address, reg, val);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic inline int gpcs_phy_read(struct mii_phy *phy, int reg)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	return phy->mdio_read(phy->dev, phy->gpcs_address, reg);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	phy->mdio_write(phy->dev, phy->gpcs_address, reg, val);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint emac_mii_reset_phy(struct mii_phy *phy)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int val;
5862306a36Sopenharmony_ci	int limit = 10000;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	val = phy_read(phy, MII_BMCR);
6162306a36Sopenharmony_ci	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
6262306a36Sopenharmony_ci	val |= BMCR_RESET;
6362306a36Sopenharmony_ci	phy_write(phy, MII_BMCR, val);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	udelay(300);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	while (--limit) {
6862306a36Sopenharmony_ci		val = phy_read(phy, MII_BMCR);
6962306a36Sopenharmony_ci		if (val >= 0 && (val & BMCR_RESET) == 0)
7062306a36Sopenharmony_ci			break;
7162306a36Sopenharmony_ci		udelay(10);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	if ((val & BMCR_ISOLATE) && limit > 0)
7462306a36Sopenharmony_ci		phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return limit <= 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint emac_mii_reset_gpcs(struct mii_phy *phy)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	int val;
8262306a36Sopenharmony_ci	int limit = 10000;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	val = gpcs_phy_read(phy, MII_BMCR);
8562306a36Sopenharmony_ci	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
8662306a36Sopenharmony_ci	val |= BMCR_RESET;
8762306a36Sopenharmony_ci	gpcs_phy_write(phy, MII_BMCR, val);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	udelay(300);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	while (--limit) {
9262306a36Sopenharmony_ci		val = gpcs_phy_read(phy, MII_BMCR);
9362306a36Sopenharmony_ci		if (val >= 0 && (val & BMCR_RESET) == 0)
9462306a36Sopenharmony_ci			break;
9562306a36Sopenharmony_ci		udelay(10);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	if ((val & BMCR_ISOLATE) && limit > 0)
9862306a36Sopenharmony_ci		gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (limit > 0 && phy->mode == PHY_INTERFACE_MODE_SGMII) {
10162306a36Sopenharmony_ci		/* Configure GPCS interface to recommended setting for SGMII */
10262306a36Sopenharmony_ci		gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */
10362306a36Sopenharmony_ci		gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */
10462306a36Sopenharmony_ci		gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX     */
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return limit <= 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int ctl, adv;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	phy->autoneg = AUTONEG_ENABLE;
11562306a36Sopenharmony_ci	phy->speed = SPEED_10;
11662306a36Sopenharmony_ci	phy->duplex = DUPLEX_HALF;
11762306a36Sopenharmony_ci	phy->pause = phy->asym_pause = 0;
11862306a36Sopenharmony_ci	phy->advertising = advertise;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ctl = phy_read(phy, MII_BMCR);
12162306a36Sopenharmony_ci	if (ctl < 0)
12262306a36Sopenharmony_ci		return ctl;
12362306a36Sopenharmony_ci	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* First clear the PHY */
12662306a36Sopenharmony_ci	phy_write(phy, MII_BMCR, ctl);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Setup standard advertise */
12962306a36Sopenharmony_ci	adv = phy_read(phy, MII_ADVERTISE);
13062306a36Sopenharmony_ci	if (adv < 0)
13162306a36Sopenharmony_ci		return adv;
13262306a36Sopenharmony_ci	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
13362306a36Sopenharmony_ci		 ADVERTISE_PAUSE_ASYM);
13462306a36Sopenharmony_ci	if (advertise & ADVERTISED_10baseT_Half)
13562306a36Sopenharmony_ci		adv |= ADVERTISE_10HALF;
13662306a36Sopenharmony_ci	if (advertise & ADVERTISED_10baseT_Full)
13762306a36Sopenharmony_ci		adv |= ADVERTISE_10FULL;
13862306a36Sopenharmony_ci	if (advertise & ADVERTISED_100baseT_Half)
13962306a36Sopenharmony_ci		adv |= ADVERTISE_100HALF;
14062306a36Sopenharmony_ci	if (advertise & ADVERTISED_100baseT_Full)
14162306a36Sopenharmony_ci		adv |= ADVERTISE_100FULL;
14262306a36Sopenharmony_ci	if (advertise & ADVERTISED_Pause)
14362306a36Sopenharmony_ci		adv |= ADVERTISE_PAUSE_CAP;
14462306a36Sopenharmony_ci	if (advertise & ADVERTISED_Asym_Pause)
14562306a36Sopenharmony_ci		adv |= ADVERTISE_PAUSE_ASYM;
14662306a36Sopenharmony_ci	phy_write(phy, MII_ADVERTISE, adv);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (phy->features &
14962306a36Sopenharmony_ci	    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
15062306a36Sopenharmony_ci		adv = phy_read(phy, MII_CTRL1000);
15162306a36Sopenharmony_ci		if (adv < 0)
15262306a36Sopenharmony_ci			return adv;
15362306a36Sopenharmony_ci		adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
15462306a36Sopenharmony_ci		if (advertise & ADVERTISED_1000baseT_Full)
15562306a36Sopenharmony_ci			adv |= ADVERTISE_1000FULL;
15662306a36Sopenharmony_ci		if (advertise & ADVERTISED_1000baseT_Half)
15762306a36Sopenharmony_ci			adv |= ADVERTISE_1000HALF;
15862306a36Sopenharmony_ci		phy_write(phy, MII_CTRL1000, adv);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Start/Restart aneg */
16262306a36Sopenharmony_ci	ctl = phy_read(phy, MII_BMCR);
16362306a36Sopenharmony_ci	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
16462306a36Sopenharmony_ci	phy_write(phy, MII_BMCR, ctl);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int ctl;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	phy->autoneg = AUTONEG_DISABLE;
17462306a36Sopenharmony_ci	phy->speed = speed;
17562306a36Sopenharmony_ci	phy->duplex = fd;
17662306a36Sopenharmony_ci	phy->pause = phy->asym_pause = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ctl = phy_read(phy, MII_BMCR);
17962306a36Sopenharmony_ci	if (ctl < 0)
18062306a36Sopenharmony_ci		return ctl;
18162306a36Sopenharmony_ci	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* First clear the PHY */
18462306a36Sopenharmony_ci	phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Select speed & duplex */
18762306a36Sopenharmony_ci	switch (speed) {
18862306a36Sopenharmony_ci	case SPEED_10:
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case SPEED_100:
19162306a36Sopenharmony_ci		ctl |= BMCR_SPEED100;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	case SPEED_1000:
19462306a36Sopenharmony_ci		ctl |= BMCR_SPEED1000;
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	default:
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	if (fd == DUPLEX_FULL)
20062306a36Sopenharmony_ci		ctl |= BMCR_FULLDPLX;
20162306a36Sopenharmony_ci	phy_write(phy, MII_BMCR, ctl);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int genmii_poll_link(struct mii_phy *phy)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int status;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Clear latched value with dummy read */
21162306a36Sopenharmony_ci	phy_read(phy, MII_BMSR);
21262306a36Sopenharmony_ci	status = phy_read(phy, MII_BMSR);
21362306a36Sopenharmony_ci	if (status < 0 || (status & BMSR_LSTATUS) == 0)
21462306a36Sopenharmony_ci		return 0;
21562306a36Sopenharmony_ci	if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE))
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci	return 1;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int genmii_read_link(struct mii_phy *phy)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	if (phy->autoneg == AUTONEG_ENABLE) {
22362306a36Sopenharmony_ci		int glpa = 0;
22462306a36Sopenharmony_ci		int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE);
22562306a36Sopenharmony_ci		if (lpa < 0)
22662306a36Sopenharmony_ci			return lpa;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		if (phy->features &
22962306a36Sopenharmony_ci		    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
23062306a36Sopenharmony_ci			int adv = phy_read(phy, MII_CTRL1000);
23162306a36Sopenharmony_ci			glpa = phy_read(phy, MII_STAT1000);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			if (glpa < 0 || adv < 0)
23462306a36Sopenharmony_ci				return adv;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			glpa &= adv << 2;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		phy->speed = SPEED_10;
24062306a36Sopenharmony_ci		phy->duplex = DUPLEX_HALF;
24162306a36Sopenharmony_ci		phy->pause = phy->asym_pause = 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		if (glpa & (LPA_1000FULL | LPA_1000HALF)) {
24462306a36Sopenharmony_ci			phy->speed = SPEED_1000;
24562306a36Sopenharmony_ci			if (glpa & LPA_1000FULL)
24662306a36Sopenharmony_ci				phy->duplex = DUPLEX_FULL;
24762306a36Sopenharmony_ci		} else if (lpa & (LPA_100FULL | LPA_100HALF)) {
24862306a36Sopenharmony_ci			phy->speed = SPEED_100;
24962306a36Sopenharmony_ci			if (lpa & LPA_100FULL)
25062306a36Sopenharmony_ci				phy->duplex = DUPLEX_FULL;
25162306a36Sopenharmony_ci		} else if (lpa & LPA_10FULL)
25262306a36Sopenharmony_ci			phy->duplex = DUPLEX_FULL;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		if (phy->duplex == DUPLEX_FULL) {
25562306a36Sopenharmony_ci			phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
25662306a36Sopenharmony_ci			phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	} else {
25962306a36Sopenharmony_ci		int bmcr = phy_read(phy, MII_BMCR);
26062306a36Sopenharmony_ci		if (bmcr < 0)
26162306a36Sopenharmony_ci			return bmcr;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (bmcr & BMCR_FULLDPLX)
26462306a36Sopenharmony_ci			phy->duplex = DUPLEX_FULL;
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			phy->duplex = DUPLEX_HALF;
26762306a36Sopenharmony_ci		if (bmcr & BMCR_SPEED1000)
26862306a36Sopenharmony_ci			phy->speed = SPEED_1000;
26962306a36Sopenharmony_ci		else if (bmcr & BMCR_SPEED100)
27062306a36Sopenharmony_ci			phy->speed = SPEED_100;
27162306a36Sopenharmony_ci		else
27262306a36Sopenharmony_ci			phy->speed = SPEED_10;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		phy->pause = phy->asym_pause = 0;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* Generic implementation for most 10/100/1000 PHYs */
28062306a36Sopenharmony_cistatic const struct mii_phy_ops generic_phy_ops = {
28162306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
28262306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
28362306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
28462306a36Sopenharmony_ci	.read_link	= genmii_read_link
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct mii_phy_def genmii_phy_def = {
28862306a36Sopenharmony_ci	.phy_id		= 0x00000000,
28962306a36Sopenharmony_ci	.phy_id_mask	= 0x00000000,
29062306a36Sopenharmony_ci	.name		= "Generic MII",
29162306a36Sopenharmony_ci	.ops		= &generic_phy_ops
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/* CIS8201 */
29562306a36Sopenharmony_ci#define MII_CIS8201_10BTCSR	0x16
29662306a36Sopenharmony_ci#define  TENBTCSR_ECHO_DISABLE	0x2000
29762306a36Sopenharmony_ci#define MII_CIS8201_EPCR	0x17
29862306a36Sopenharmony_ci#define  EPCR_MODE_MASK		0x3000
29962306a36Sopenharmony_ci#define  EPCR_GMII_MODE		0x0000
30062306a36Sopenharmony_ci#define  EPCR_RGMII_MODE	0x1000
30162306a36Sopenharmony_ci#define  EPCR_TBI_MODE		0x2000
30262306a36Sopenharmony_ci#define  EPCR_RTBI_MODE		0x3000
30362306a36Sopenharmony_ci#define MII_CIS8201_ACSR	0x1c
30462306a36Sopenharmony_ci#define  ACSR_PIN_PRIO_SELECT	0x0004
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int cis8201_init(struct mii_phy *phy)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	int epcr;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	epcr = phy_read(phy, MII_CIS8201_EPCR);
31162306a36Sopenharmony_ci	if (epcr < 0)
31262306a36Sopenharmony_ci		return epcr;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	epcr &= ~EPCR_MODE_MASK;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	switch (phy->mode) {
31762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_TBI:
31862306a36Sopenharmony_ci		epcr |= EPCR_TBI_MODE;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RTBI:
32162306a36Sopenharmony_ci		epcr |= EPCR_RTBI_MODE;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_GMII:
32462306a36Sopenharmony_ci		epcr |= EPCR_GMII_MODE;
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
32762306a36Sopenharmony_ci	default:
32862306a36Sopenharmony_ci		epcr |= EPCR_RGMII_MODE;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	phy_write(phy, MII_CIS8201_EPCR, epcr);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* MII regs override strap pins */
33462306a36Sopenharmony_ci	phy_write(phy, MII_CIS8201_ACSR,
33562306a36Sopenharmony_ci		  phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */
33862306a36Sopenharmony_ci	phy_write(phy, MII_CIS8201_10BTCSR,
33962306a36Sopenharmony_ci		  phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic const struct mii_phy_ops cis8201_phy_ops = {
34562306a36Sopenharmony_ci	.init		= cis8201_init,
34662306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
34762306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
34862306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
34962306a36Sopenharmony_ci	.read_link	= genmii_read_link
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct mii_phy_def cis8201_phy_def = {
35362306a36Sopenharmony_ci	.phy_id		= 0x000fc410,
35462306a36Sopenharmony_ci	.phy_id_mask	= 0x000ffff0,
35562306a36Sopenharmony_ci	.name		= "CIS8201 Gigabit Ethernet",
35662306a36Sopenharmony_ci	.ops		= &cis8201_phy_ops
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct mii_phy_def bcm5248_phy_def = {
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	.phy_id		= 0x0143bc00,
36262306a36Sopenharmony_ci	.phy_id_mask	= 0x0ffffff0,
36362306a36Sopenharmony_ci	.name		= "BCM5248 10/100 SMII Ethernet",
36462306a36Sopenharmony_ci	.ops		= &generic_phy_ops
36562306a36Sopenharmony_ci};
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int m88e1111_init(struct mii_phy *phy)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	pr_debug("%s: Marvell 88E1111 Ethernet\n", __func__);
37062306a36Sopenharmony_ci	phy_write(phy, 0x14, 0x0ce3);
37162306a36Sopenharmony_ci	phy_write(phy, 0x18, 0x4101);
37262306a36Sopenharmony_ci	phy_write(phy, 0x09, 0x0e00);
37362306a36Sopenharmony_ci	phy_write(phy, 0x04, 0x01e1);
37462306a36Sopenharmony_ci	phy_write(phy, 0x00, 0x9140);
37562306a36Sopenharmony_ci	phy_write(phy, 0x00, 0x1140);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return  0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int m88e1112_init(struct mii_phy *phy)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	/*
38362306a36Sopenharmony_ci	 * Marvell 88E1112 PHY needs to have the SGMII MAC
38462306a36Sopenharmony_ci	 * interace (page 2) properly configured to
38562306a36Sopenharmony_ci	 * communicate with the 460EX/GT GPCS interface.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	u16 reg_short;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Set access to Page 2 */
39362306a36Sopenharmony_ci	phy_write(phy, 0x16, 0x0002);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	phy_write(phy, 0x00, 0x0040); /* 1Gbps */
39662306a36Sopenharmony_ci	reg_short = (u16)(phy_read(phy, 0x1a));
39762306a36Sopenharmony_ci	reg_short |= 0x8000; /* bypass Auto-Negotiation */
39862306a36Sopenharmony_ci	phy_write(phy, 0x1a, reg_short);
39962306a36Sopenharmony_ci	emac_mii_reset_phy(phy); /* reset MAC interface */
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Reset access to Page 0 */
40262306a36Sopenharmony_ci	phy_write(phy, 0x16, 0x0000);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return  0;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int et1011c_init(struct mii_phy *phy)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	u16 reg_short;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	reg_short = (u16)(phy_read(phy, 0x16));
41262306a36Sopenharmony_ci	reg_short &= ~(0x7);
41362306a36Sopenharmony_ci	reg_short |= 0x6;	/* RGMII Trace Delay*/
41462306a36Sopenharmony_ci	phy_write(phy, 0x16, reg_short);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	reg_short = (u16)(phy_read(phy, 0x17));
41762306a36Sopenharmony_ci	reg_short &= ~(0x40);
41862306a36Sopenharmony_ci	phy_write(phy, 0x17, reg_short);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	phy_write(phy, 0x1c, 0x74f0);
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic const struct mii_phy_ops et1011c_phy_ops = {
42562306a36Sopenharmony_ci	.init		= et1011c_init,
42662306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
42762306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
42862306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
42962306a36Sopenharmony_ci	.read_link	= genmii_read_link
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic struct mii_phy_def et1011c_phy_def = {
43362306a36Sopenharmony_ci	.phy_id		= 0x0282f000,
43462306a36Sopenharmony_ci	.phy_id_mask	= 0x0fffff00,
43562306a36Sopenharmony_ci	.name		= "ET1011C Gigabit Ethernet",
43662306a36Sopenharmony_ci	.ops		= &et1011c_phy_ops
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic const struct mii_phy_ops m88e1111_phy_ops = {
44462306a36Sopenharmony_ci	.init		= m88e1111_init,
44562306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
44662306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
44762306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
44862306a36Sopenharmony_ci	.read_link	= genmii_read_link
44962306a36Sopenharmony_ci};
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic struct mii_phy_def m88e1111_phy_def = {
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	.phy_id		= 0x01410CC0,
45462306a36Sopenharmony_ci	.phy_id_mask	= 0x0ffffff0,
45562306a36Sopenharmony_ci	.name		= "Marvell 88E1111 Ethernet",
45662306a36Sopenharmony_ci	.ops		= &m88e1111_phy_ops,
45762306a36Sopenharmony_ci};
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic const struct mii_phy_ops m88e1112_phy_ops = {
46062306a36Sopenharmony_ci	.init		= m88e1112_init,
46162306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
46262306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
46362306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
46462306a36Sopenharmony_ci	.read_link	= genmii_read_link
46562306a36Sopenharmony_ci};
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic struct mii_phy_def m88e1112_phy_def = {
46862306a36Sopenharmony_ci	.phy_id		= 0x01410C90,
46962306a36Sopenharmony_ci	.phy_id_mask	= 0x0ffffff0,
47062306a36Sopenharmony_ci	.name		= "Marvell 88E1112 Ethernet",
47162306a36Sopenharmony_ci	.ops		= &m88e1112_phy_ops,
47262306a36Sopenharmony_ci};
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int ar8035_init(struct mii_phy *phy)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */
47762306a36Sopenharmony_ci	phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */
47862306a36Sopenharmony_ci	phy_write(phy, 0x1d, 0xb);    /* Address hib ctrl */
47962306a36Sopenharmony_ci	phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const struct mii_phy_ops ar8035_phy_ops = {
48562306a36Sopenharmony_ci	.init		= ar8035_init,
48662306a36Sopenharmony_ci	.setup_aneg	= genmii_setup_aneg,
48762306a36Sopenharmony_ci	.setup_forced	= genmii_setup_forced,
48862306a36Sopenharmony_ci	.poll_link	= genmii_poll_link,
48962306a36Sopenharmony_ci	.read_link	= genmii_read_link,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic struct mii_phy_def ar8035_phy_def = {
49362306a36Sopenharmony_ci	.phy_id		= 0x004dd070,
49462306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
49562306a36Sopenharmony_ci	.name		= "Atheros 8035 Gigabit Ethernet",
49662306a36Sopenharmony_ci	.ops		= &ar8035_phy_ops,
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic struct mii_phy_def *mii_phy_table[] = {
50062306a36Sopenharmony_ci	&et1011c_phy_def,
50162306a36Sopenharmony_ci	&cis8201_phy_def,
50262306a36Sopenharmony_ci	&bcm5248_phy_def,
50362306a36Sopenharmony_ci	&m88e1111_phy_def,
50462306a36Sopenharmony_ci	&m88e1112_phy_def,
50562306a36Sopenharmony_ci	&ar8035_phy_def,
50662306a36Sopenharmony_ci	&genmii_phy_def,
50762306a36Sopenharmony_ci	NULL
50862306a36Sopenharmony_ci};
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ciint emac_mii_phy_probe(struct mii_phy *phy, int address)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct mii_phy_def *def;
51362306a36Sopenharmony_ci	int i;
51462306a36Sopenharmony_ci	u32 id;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	phy->autoneg = AUTONEG_DISABLE;
51762306a36Sopenharmony_ci	phy->advertising = 0;
51862306a36Sopenharmony_ci	phy->address = address;
51962306a36Sopenharmony_ci	phy->speed = SPEED_10;
52062306a36Sopenharmony_ci	phy->duplex = DUPLEX_HALF;
52162306a36Sopenharmony_ci	phy->pause = phy->asym_pause = 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Take PHY out of isolate mode and reset it. */
52462306a36Sopenharmony_ci	if (emac_mii_reset_phy(phy))
52562306a36Sopenharmony_ci		return -ENODEV;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Read ID and find matching entry */
52862306a36Sopenharmony_ci	id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
52962306a36Sopenharmony_ci	for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
53062306a36Sopenharmony_ci		if ((id & def->phy_id_mask) == def->phy_id)
53162306a36Sopenharmony_ci			break;
53262306a36Sopenharmony_ci	/* Should never be NULL (we have a generic entry), but... */
53362306a36Sopenharmony_ci	if (!def)
53462306a36Sopenharmony_ci		return -ENODEV;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	phy->def = def;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* Determine PHY features if needed */
53962306a36Sopenharmony_ci	phy->features = def->features;
54062306a36Sopenharmony_ci	if (!phy->features) {
54162306a36Sopenharmony_ci		u16 bmsr = phy_read(phy, MII_BMSR);
54262306a36Sopenharmony_ci		if (bmsr & BMSR_ANEGCAPABLE)
54362306a36Sopenharmony_ci			phy->features |= SUPPORTED_Autoneg;
54462306a36Sopenharmony_ci		if (bmsr & BMSR_10HALF)
54562306a36Sopenharmony_ci			phy->features |= SUPPORTED_10baseT_Half;
54662306a36Sopenharmony_ci		if (bmsr & BMSR_10FULL)
54762306a36Sopenharmony_ci			phy->features |= SUPPORTED_10baseT_Full;
54862306a36Sopenharmony_ci		if (bmsr & BMSR_100HALF)
54962306a36Sopenharmony_ci			phy->features |= SUPPORTED_100baseT_Half;
55062306a36Sopenharmony_ci		if (bmsr & BMSR_100FULL)
55162306a36Sopenharmony_ci			phy->features |= SUPPORTED_100baseT_Full;
55262306a36Sopenharmony_ci		if (bmsr & BMSR_ESTATEN) {
55362306a36Sopenharmony_ci			u16 esr = phy_read(phy, MII_ESTATUS);
55462306a36Sopenharmony_ci			if (esr & ESTATUS_1000_TFULL)
55562306a36Sopenharmony_ci				phy->features |= SUPPORTED_1000baseT_Full;
55662306a36Sopenharmony_ci			if (esr & ESTATUS_1000_THALF)
55762306a36Sopenharmony_ci				phy->features |= SUPPORTED_1000baseT_Half;
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci		phy->features |= SUPPORTED_MII;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Setup default advertising */
56362306a36Sopenharmony_ci	phy->advertising = phy->features;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
569