Files
2026-01-12 18:46:01 +08:00

317 lines
7.5 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
#define MII__BMSR 0x01
#define MII__PHYSID1 0x02
#define MII__PHYSID2 0x03
#define MII__ADVERTISE 0x04
#define MII__LPA 0x05
#define MII__EXPANSION 0x06
#define MII__PHYSTS 0x10
#define MII__PHYCR 0x19
/* Basic mode control register. */
#define BMCR__RESV 0x003f
#define BMCR__SPEED1000 0x0040
#define BMCR__CTST 0x0080
#define BMCR__FULLDPLX 0x0100
#define BMCR__ANRESTART 0x0200
#define BMCR__ISOLATE 0x0400
#define BMCR__PDOWN 0x0800
#define BMCR__ANENABLE 0x1000
#define BMCR__SPEED100 0x2000
#define BMCR__LOOPBACK 0x4000
#define BMCR__RESET 0x8000
#define BMCR__SPEED10 0x0000
/* Basic mode status register */
#define BMSR_LINK_STATUS 0x4
/* Advertisement control register. */
#define ADVERTISE__SLCT 0x001f
#define ADVERTISE__CSMA 0x0001
#define ADVERTISE__10HALF 0x0020
#define ADVERTISE__1000XFULL 0x0020
#define ADVERTISE__10FULL 0x0040
#define ADVERTISE__1000XHALF 0x0040
#define ADVERTISE__100HALF 0x0080
#define ADVERTISE__1000XPAUSE 0x0080
#define ADVERTISE__100FULL 0x0100
#define ADVERTISE__1000XPSE_ASYM 0x0100
#define ADVERTISE__100BASE4 0x0200
#define ADVERTISE__PAUSE_CAP 0x0400
#define ADVERTISE__PAUSE_ASYM 0x0800
#define ADVERTISE__RESV 0x1000
#define ADVERTISE__RFAULT 0x2000
#define ADVERTISE__LPACK 0x4000
#define ADVERTISE__NPAGE 0x8000
/* 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