Files
E3/e3_176_ref/drivers/source/eth/phy/dp83848.c
2025-10-21 19:40:27 +08:00

317 lines
8.7 KiB
C

/*
* dp83848.c
*
* Copyright (c) 2020 Semidrive Semiconductor.
* All rights reserved.
*
* Description: eth phy dp83848 drv
*
* Revision History:
* -----------------
*/
#include <stdlib.h>
#include "debug.h"
#include "CLI.h"
#include "phy.h"
#define MII_BMCR 0x00 /* Basic mode control register */
#define MII_BMSR 0x01 /* Basic mode status register */
#define MII_PHYSID1 0x02 /* PHYS ID 1 */
#define MII_PHYSID2 0x03 /* PHYS ID 2 */
#define MII_ADVERTISE 0x04 /* Advertisement control reg */
#define MII_LPA 0x05 /* Link partner ability reg */
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_PHYSTS 0x10
#define MII_PHYCR 0x19
/* Basic mode control register. */
#define BMCR_RESV 0x003f /* Unused... */
#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
#define BMCR_ISOLATE 0x0400 /* Isolate data paths from MII */
#define BMCR_PDOWN 0x0800 /* Enable low power state */
#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
#define BMCR_RESET 0x8000 /* Reset to default state */
#define BMCR_SPEED10 0x0000 /* Select 10Mbps */
/* Basic mode status register */
#define BMSR_LINK_STATUS 0x4
/* Advertisement control register. */
#define ADVERTISE_SLCT 0x001f /* Selector bits */
#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */
#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */
#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */
#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */
#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */
#define ADVERTISE_RESV 0x1000 /* Unused... */
#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
/* PHY status register */
#define PHYSTS_DUPLEX 0x40
#define PHYSTS_SPEED 0x20
/* PHY control register */
#define PHYCR_MDIX_EN 0x8000
#define PHY_CLI_EN 1
#if PHY_CLI_EN
static phy_dev_t *g_dp83848_dev;
#endif
static int dp83848_read(phy_dev_t *dev, uint32_t regaddr)
{
phy_bus_t *bus = dev->bus;
int ret = bus->ops->mdio_read(bus, dev->phy_addr, 0xFF, regaddr);
if (ret < 0)
ssdk_printf(SSDK_ERR, "dp83848_read err!\n");
return ret;
}
static int dp83848_write(phy_dev_t *dev, uint32_t regaddr, uint16_t val)
{
phy_bus_t *bus = dev->bus;
if (bus->ops->mdio_write(bus, dev->phy_addr, 0xFF, regaddr, val)) {
ssdk_printf(SSDK_ERR, "dp83848_write err!\n");
return -1;
}
return 0;
}
static int dp83848_reset(phy_dev_t *dev)
{
int timeout = 500;
int reg_val;
if (dp83848_write(dev, MII_BMCR, BMCR_RESET)) {
ssdk_printf(SSDK_ERR, "PHY reset failed\n");
return -1;
}
while (timeout--) {
reg_val = dp83848_read(dev, MII_BMCR);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "BMCR read failed\n");
return -2;
}
if (!(reg_val & BMCR_RESET))
break;
udelay(1000);
}
if (reg_val & BMCR_RESET) {
ssdk_printf(SSDK_ERR, "PHY reset timeout\n");
return -3;
}
return 0;
}
static uint32_t dp83848_get_phy_id(phy_dev_t *dev)
{
int reg_val;
reg_val = dp83848_read(dev, MII_PHYSID1);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read phy id1 error\n");
return reg_val;
}
reg_val <<= 16;
int temp = dp83848_read(dev, MII_PHYSID2);
if (temp < 0) {
ssdk_printf(SSDK_ERR, "Read phy id2 error\n");
return temp;
}
reg_val |= temp;
return reg_val;
}
static int dp83848_config_aneg(phy_dev_t *dev)
{
int reg_val, ret, timeout = 1000;
if (dev->auto_negotiation) {
reg_val = dp83848_read(dev, MII_ADVERTISE);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read advertise error\n");
return reg_val;
}
reg_val |= ADVERTISE_100FULL | ADVERTISE_100HALF |
ADVERTISE_10FULL | ADVERTISE_10HALF;
ret = dp83848_write(dev, MII_ADVERTISE, reg_val);
if (ret) {
ssdk_printf(SSDK_ERR, "Config advertisement register failed\n");
return ret;
}
reg_val = dp83848_read(dev, MII_BMCR);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read BMCR failed\n");
return reg_val;
}
reg_val |= BMCR_ANENABLE;
ret = dp83848_write(dev, MII_BMCR, reg_val);
if (ret)
ssdk_printf(SSDK_ERR, "Write BMCR failed\n");
}
else {
reg_val = dp83848_read(dev, MII_BMCR);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read BMCR failed\n");
return reg_val;
}
reg_val &= ~BMCR_ANENABLE;
if (dev->speed == PHY_SPEED_10)
reg_val &= ~BMCR_SPEED100;
else
reg_val |= BMCR_SPEED100;
if (dev->duplex_mode == ETH_PHY_DUPLEX_MODE_HALF)
reg_val &= ~BMCR_FULLDPLX;
else
reg_val |= BMCR_FULLDPLX;
ret = dp83848_write(dev, MII_BMCR, reg_val);
if (ret)
ssdk_printf(SSDK_ERR, "Write BMCR failed\n");
}
if (ret)
return ret;
if (dev->auto_negotiation) {
ssdk_printf(SSDK_EMERG, "Wait for dp83848 link ...\n");
while (timeout--) {
reg_val = dp83848_read(dev, MII_BMSR);
if ((reg_val >= 0) && (reg_val & BMSR_LINK_STATUS))
break;
udelay(1);
}
if ((reg_val < 0) || !(reg_val & BMSR_LINK_STATUS)) {
ssdk_printf(SSDK_EMERG, "Link failed\n");
return -1;
}
else
ssdk_printf(SSDK_EMERG, "Link successfully\n");
}
return 0;
}
static int dp83848_startup(phy_dev_t *dev)
{
int reg_val, ret;
reg_val = dp83848_read(dev, MII_PHYSTS);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read PHYSTS failed\n");
return reg_val;
}
ssdk_printf(SSDK_INFO, "PHYSTS value 0x%x\n", reg_val);
if (reg_val & PHYSTS_SPEED)
dev->speed = PHY_SPEED_10;
else
dev->speed = PHY_SPEED_100;
if (reg_val & PHYSTS_DUPLEX)
dev->duplex_mode = ETH_PHY_DUPLEX_MODE_FULL;
else
dev->duplex_mode = ETH_PHY_DUPLEX_MODE_HALF;
reg_val = dp83848_read(dev, MII_BMCR);
if (reg_val < 0) {
ssdk_printf(SSDK_ERR, "Read BMCR failed\n");
return reg_val;
}
reg_val &= ~(BMCR_ISOLATE | BMCR_PDOWN);
ret = dp83848_write(dev, MII_BMCR, reg_val);
if (ret) {
ssdk_printf(SSDK_ERR, "Config BMCR failed\n");
goto out;
}
ret = dp83848_write(dev, MII_PHYCR, PHYCR_MDIX_EN | dev->phy_addr);
if (ret) {
ssdk_printf(SSDK_ERR, "Config PHYCR failed\n");
goto out;
}
ssdk_printf(SSDK_INFO, "PHY status 0x%x\n", dp83848_read(dev, MII_BMSR));
out:
return ret;
}
int dp83848_init(phy_dev_t *dev)
{
#if PHY_CLI_EN
g_dp83848_dev = dev;
#endif
if (dp83848_reset(dev))
return -1;
ssdk_printf(SSDK_INFO, "PHY ID 0x%x\n",
dp83848_get_phy_id(dev));
if (dp83848_config_aneg(dev))
return -2;
return dp83848_startup(dev);
}
#if PHY_CLI_EN
static int phy_dump(int argc, char *argv[])
{
if (!g_dp83848_dev) {
printf("dp83848 is not availiable\n");
return 0;
}
if (argc == 0) {
printf("phy <reg_num> [val]\n");
return 0;
}
int reg_num = strtoul(argv[0], NULL, 0);
if (argc == 1)
printf("read reg %d: 0x%x\n", reg_num, dp83848_read(g_dp83848_dev, reg_num));
else {
int val = strtoul(argv[1], NULL, 0);
printf("write reg %d val 0x%x result %d\n", reg_num, val,
dp83848_write(g_dp83848_dev, reg_num, val));
}
return 0;
}
CLI_CMD("phy", "\r\nphy:\r\n phy reg dump\r\n", phy_dump);
#endif