/* * dp83848.c * * Copyright (c) 2020 Semidrive Semiconductor. * All rights reserved. * * Description: eth phy dp83848 drv * * Revision History: * ----------------- */ #include #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 [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