mccomphy: add support for YT8531

Reviewed by:	mhorne
Tested by:	mhorne
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D45600
This commit is contained in:
Jari Sihvola 2025-03-29 17:55:13 -03:00 committed by Mitchell Horne
parent f01d26dec6
commit e69623451e
2 changed files with 236 additions and 26 deletions

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2022 Jared McNeill <jmcneill@invisible.ca>
* Copyright (c) 2022 Soren Schmidt <sos@deepcore.dk>
* Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,7 +27,8 @@
*/
/*
* Motorcomm YT8511C / YT8511H Integrated 10/100/1000 Gigabit Ethernet phy
* Motorcomm YT8511C/YT8511H/YT8531
* Integrated 10/100/1000 Gigabit Ethernet phy
*/
#include <sys/param.h>
@ -42,12 +44,16 @@
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/mii/mii_fdt.h>
#include "miidevs.h"
#include "miibus_if.h"
#define MCOMMPHY_OUI 0x000000
#define MCOMMPHY_MODEL 0x10
#define MCOMMPHY_REV 0x0a
#define MCOMMPHY_YT8511_OUI 0x000000
#define MCOMMPHY_YT8511_MODEL 0x10
#define MCOMMPHY_YT8511_REV 0x0a
#define MCOMMPHY_YT8531_MODEL 0x11
#define EXT_REG_ADDR 0x1e
#define EXT_REG_DATA 0x1f
@ -61,9 +67,49 @@
#define PHY_SLEEP_CONTROL1_REG 0x27
#define PLLON_IN_SLP 0x4000
/* Registers and values for YT8531 */
#define YT8531_CHIP_CONFIG 0xa001
#define RXC_DLY_EN (1 << 8)
#define YT8531_PAD_DRSTR_CFG 0xa010
#define PAD_RXC_MASK 0x7
#define PAD_RXC_SHIFT 13
#define JH7110_RGMII_RXC_STRENGTH 6
#define YT8531_RGMII_CONFIG1 0xa003
#define RX_DELAY_SEL_SHIFT 10
#define RX_DELAY_SEL_MASK 0xf
#define RXC_DLY_THRESH 2250
#define RXC_DLY_ADDON 1900
#define TX_DELAY_SEL_FE_MASK 0xf
#define TX_DELAY_SEL_FE_SHIFT 4
#define TX_DELAY_SEL_MASK 0xf
#define TX_DELAY_SEL_SHIFT 0
#define TX_CLK_SEL (1 << 14)
#define INTERNAL_DLY_DIV 150
#define YT8531_SYNCE_CFG 0xa012
#define EN_SYNC_E (1 << 6)
#define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask))
#define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask))
static const struct mii_phydesc mcommphys[] = {
MII_PHY_DESC(MOTORCOMM, YT8511),
MII_PHY_DESC(MOTORCOMM2, YT8531),
MII_PHY_END
};
struct mcommphy_softc {
mii_softc_t mii_sc;
device_t dev;
bool tx_10_inv;
bool tx_100_inv;
bool tx_1000_inv;
};
static void mcommphy_yt8531_speed_adjustment(struct mii_softc *sc);
static int
mcommphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
{
@ -84,6 +130,16 @@ mcommphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
/* Update the media status. */
PHY_STATUS(sc);
/*
* For the needs of JH7110 which has two Ethernet devices with
* different TX inverted configuration depending on speed used
*/
if (sc->mii_mpd_model == MCOMMPHY_YT8531_MODEL &&
(sc->mii_media_active != mii->mii_media_active ||
sc->mii_media_status != mii->mii_media_status)) {
mcommphy_yt8531_speed_adjustment(sc);
}
/* Callback if something changed. */
mii_phy_update(sc, cmd);
@ -105,26 +161,22 @@ mcommphy_probe(device_t dev)
* The YT8511C reports an OUI of 0. Best we can do here is to match
* exactly the contents of the PHY identification registers.
*/
if (MII_OUI(ma->mii_id1, ma->mii_id2) == MCOMMPHY_OUI &&
MII_MODEL(ma->mii_id2) == MCOMMPHY_MODEL &&
MII_REV(ma->mii_id2) == MCOMMPHY_REV) {
if (MII_OUI(ma->mii_id1, ma->mii_id2) == MCOMMPHY_YT8511_OUI &&
MII_MODEL(ma->mii_id2) == MCOMMPHY_YT8511_MODEL &&
MII_REV(ma->mii_id2) == MCOMMPHY_YT8511_REV) {
device_set_desc(dev, "Motorcomm YT8511 media interface");
return BUS_PROBE_DEFAULT;
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
/* YT8531 follows a conventional procedure */
return (mii_phy_dev_probe(dev, mcommphys, BUS_PROBE_DEFAULT));
}
static int
mcommphy_attach(device_t dev)
static void
mcommphy_yt8511_setup(struct mii_softc *sc)
{
struct mii_softc *sc = device_get_softc(dev);
uint16_t oldaddr, data;
mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mcommphy_funcs, 0);
PHY_RESET(sc);
/* begin chip stuff */
oldaddr = PHY_READ(sc, EXT_REG_ADDR);
PHY_WRITE(sc, EXT_REG_ADDR, PHY_CLOCK_GATING_REG);
@ -150,20 +202,176 @@ mcommphy_attach(device_t dev)
PHY_WRITE(sc, EXT_REG_DATA, data);
PHY_WRITE(sc, EXT_REG_ADDR, oldaddr);
/* end chip stuff */
}
sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
if (sc->mii_capabilities & BMSR_EXTSTAT)
sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
device_printf(dev, " ");
mii_phy_add_media(sc);
printf("\n");
static void
mcommphy_yt8531_speed_adjustment(struct mii_softc *sc)
{
struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc;
struct mii_data *mii = sc->mii_pdata;
bool tx_clk_inv = false;
uint16_t reg, oldaddr;
MIIBUS_MEDIAINIT(sc->mii_dev);
switch (IFM_SUBTYPE(mii->mii_media_active)) {
case IFM_1000_T:
tx_clk_inv = mcomm_sc->tx_1000_inv;
break;
case IFM_100_T:
tx_clk_inv = mcomm_sc->tx_100_inv;
break;
case IFM_10_T:
tx_clk_inv = mcomm_sc->tx_10_inv;
break;
}
oldaddr = PHY_READ(sc, EXT_REG_ADDR);
PHY_WRITE(sc, EXT_REG_ADDR, YT8531_RGMII_CONFIG1);
reg = PHY_READ(sc, EXT_REG_DATA);
if (tx_clk_inv)
reg |= TX_CLK_SEL;
else
reg &= ~TX_CLK_SEL;
PHY_WRITE(sc, EXT_REG_DATA, reg);
PHY_WRITE(sc, EXT_REG_ADDR, oldaddr);
return;
}
static int
mcommphy_yt8531_setup_delay(struct mii_softc *sc)
{
struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc;
mii_fdt_phy_config_t *cfg = mii_fdt_get_config(mcomm_sc->dev);
pcell_t val;
uint16_t reg, oldaddr;
int rx_delay, tx_delay = 0;
bool rxc_dly_en_off = false;
if (OF_getencprop(cfg->phynode, "rx-internal-delay-ps", &val,
sizeof(val)) > 0) {
if (val <= RXC_DLY_THRESH && val % INTERNAL_DLY_DIV == 0) {
rx_delay = val / INTERNAL_DLY_DIV;
rxc_dly_en_off = true;
} else {
rx_delay = (val - RXC_DLY_ADDON) / INTERNAL_DLY_DIV;
if ((val - RXC_DLY_ADDON) % INTERNAL_DLY_DIV != 0)
return (ENXIO);
}
}
if (OF_getencprop(cfg->phynode, "tx-internal-delay-ps", &val,
sizeof(val)) > 0) {
tx_delay = val / INTERNAL_DLY_DIV;
if (val % INTERNAL_DLY_DIV != 0)
return (ENXIO);
}
mii_fdt_free_config(cfg);
oldaddr = PHY_READ(sc, EXT_REG_ADDR);
/* Modifying Chip Config register */
PHY_WRITE(sc, EXT_REG_ADDR, YT8531_CHIP_CONFIG);
reg = PHY_READ(sc, EXT_REG_DATA);
if (rxc_dly_en_off)
reg &= ~(RXC_DLY_EN);
PHY_WRITE(sc, EXT_REG_DATA, reg);
/* Modifying RGMII Config1 register */
PHY_WRITE(sc, EXT_REG_ADDR, YT8531_RGMII_CONFIG1);
reg = PHY_READ(sc, EXT_REG_DATA);
reg &= ~(RX_DELAY_SEL_MASK << RX_DELAY_SEL_SHIFT);
reg |= rx_delay << RX_DELAY_SEL_SHIFT;
reg &= ~(TX_DELAY_SEL_MASK << TX_DELAY_SEL_SHIFT);
reg |= tx_delay << TX_DELAY_SEL_SHIFT;
PHY_WRITE(sc, EXT_REG_DATA, reg);
PHY_WRITE(sc, EXT_REG_ADDR, oldaddr);
return (0);
}
static int
mcommphy_yt8531_setup(struct mii_softc *sc)
{
struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc;
mii_fdt_phy_config_t *cfg = mii_fdt_get_config(mcomm_sc->dev);
uint16_t reg, oldaddr;
if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-10-inverted"))
mcomm_sc->tx_10_inv = true;
if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-100-inverted"))
mcomm_sc->tx_100_inv = true;
if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-1000-inverted"))
mcomm_sc->tx_1000_inv = true;
mii_fdt_free_config(cfg);
oldaddr = PHY_READ(sc, EXT_REG_ADDR);
/* Modifying Pad Drive Strength register */
PHY_WRITE(sc, EXT_REG_ADDR, YT8531_PAD_DRSTR_CFG);
reg = PHY_READ(sc, EXT_REG_DATA);
reg &= ~(PAD_RXC_MASK << PAD_RXC_SHIFT);
reg |= (JH7110_RGMII_RXC_STRENGTH << PAD_RXC_SHIFT);
PHY_WRITE(sc, EXT_REG_DATA, reg);
/* Modifying SyncE Config register */
PHY_WRITE(sc, EXT_REG_ADDR, YT8531_SYNCE_CFG);
reg = PHY_READ(sc, EXT_REG_DATA);
reg &= ~(EN_SYNC_E);
PHY_WRITE(sc, EXT_REG_DATA, reg);
PHY_WRITE(sc, EXT_REG_ADDR, oldaddr);
if (mcommphy_yt8531_setup_delay(sc) != 0)
return (ENXIO);
return (0);
}
static int
mcommphy_attach(device_t dev)
{
struct mcommphy_softc *mcomm_sc = device_get_softc(dev);
mii_softc_t *mii_sc = &mcomm_sc->mii_sc;
int ret = 0;
mcomm_sc->dev = dev;
mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mcommphy_funcs, 0);
PHY_RESET(mii_sc);
if (mii_sc->mii_mpd_model == MCOMMPHY_YT8511_MODEL)
mcommphy_yt8511_setup(mii_sc);
else if (mii_sc->mii_mpd_model == MCOMMPHY_YT8531_MODEL)
ret = mcommphy_yt8531_setup(mii_sc);
else {
device_printf(dev, "no PHY model detected\n");
return (ENXIO);
}
if (ret) {
device_printf(dev, "PHY setup failed, error: %d\n", ret);
return (ret);
}
mii_sc->mii_capabilities = PHY_READ(mii_sc, MII_BMSR) &
mii_sc->mii_capmask;
if (mii_sc->mii_capabilities & BMSR_EXTSTAT)
mii_sc->mii_extcapabilities = PHY_READ(mii_sc, MII_EXTSR);
device_printf(dev, " ");
mii_phy_add_media(mii_sc);
printf("\n");
MIIBUS_MEDIAINIT(mii_sc->mii_dev);
return (0);
}
static device_method_t mcommphy_methods[] = {
/* device interface */
@ -177,7 +385,7 @@ static device_method_t mcommphy_methods[] = {
static driver_t mcommphy_driver = {
"mcommphy",
mcommphy_methods,
sizeof(struct mii_softc)
sizeof(struct mcommphy_softc)
};
DRIVER_MODULE(mcommphy, miibus, mcommphy_driver, 0, 0);

View file

@ -63,6 +63,7 @@ oui LEVEL1 0x00207b Level 1
oui MARVELL 0x005043 Marvell Semiconductor
oui MICREL 0x0010a1 Micrel
oui MOTORCOMM 0x000000 Motorcomm
oui MOTORCOMM2 0xc82b5e Motorcomm
oui MYSON 0x00c0b4 Myson Technology
oui NATSEMI 0x080017 National Semiconductor
oui PMCSIERRA 0x00e004 PMC-Sierra
@ -295,6 +296,7 @@ model MICREL KSZ9031 0x0022 Micrel KSZ9031 10/100/1000 PHY
/* Motorcomm PHYs */
model MOTORCOMM YT8511 0x010a Motorcomm YT8511 10/100/1000 PHY
model MOTORCOMM2 YT8531 0x0011 Motorcomm YT8531 10/100/1000 PHY
/* Myson Technology PHYs */
model xxMYSON MTD972 0x0000 MTD972 10/100 media interface