From 14edc91f970c42b55a4ab680fe014374a626ba0d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Jul 2007 18:02:06 +0200 Subject: svn_rev_572 add miiphy support --- drivers/net/Kconfig | 4 ++ drivers/net/Makefile | 1 + drivers/net/miiphy.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/mii_phy.h | 97 +++++++++++++++++++++++++++++- 4 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 drivers/net/miiphy.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 93443a5aef..7c2ef080e0 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -7,6 +7,9 @@ config HAS_DM9000 config HAS_AT91_ETHER bool +config MIIPHY + bool + menu "Network drivers " depends on NET @@ -25,6 +28,7 @@ config DRIVER_NET_AT91_ETHER config DRIVER_NET_MPC5200 bool "MPC5200 Ethernet driver" depends on ARCH_MPC5200 + select MIIPHY config DRIVER_NET_TAP bool "tap Ethernet driver" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 189896e71a..62745b78b7 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_DRIVER_NET_DM9000) += dm9000.o obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o +obj-$(CONFIG_MIIPHY) += miiphy.o diff --git a/drivers/net/miiphy.c b/drivers/net/miiphy.c new file mode 100644 index 0000000000..8f810f54d1 --- /dev/null +++ b/drivers/net/miiphy.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +int miiphy_restart_aneg(struct miiphy_device *mdev) +{ + uint16_t status; + int timeout; + + /* + * Reset PHY, then delay 300ns + */ + mdev->write(mdev, mdev->address, PHY_BMCR, PHY_BMCR_RESET); + udelay(1000); + + if (mdev->flags & MIIPHY_FORCE_10) { + printf("Forcing 10 Mbps ethernet link... "); + mdev->read(mdev, mdev->address, PHY_BMSR, &status); + mdev->write(mdev, mdev->address, PHY_BMCR, PHY_BMCR_DPLX | PHY_BMCR_COL_TST); + + timeout = 20; + do { /* wait for link status to go down */ + udelay(10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("hmmm, should not have waited..."); +#endif + break; + } + mdev->read(mdev, mdev->address, PHY_BMSR, &status); + } while (status & PHY_BMSR_LS); + + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + mdev->write(mdev, mdev->address, PHY_ANAR, 0x01e1); + + mdev->write(mdev, mdev->address, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + } + + return 0; +} + +int miiphy_wait_aneg(struct miiphy_device *mdev) +{ + int timeout = 1; + uint16_t status; + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay(1000); + + if (!timeout--) { + printf("%s: Autonegotiation timeout\n", mdev->dev.id); + return -1; + } + + if (mdev->read(mdev, mdev->address, PHY_BMSR, &status)) { + printf("%s: Autonegotiation failed. status: 0x%04x\n", mdev->dev.id, status); + return -1; + } + } while (!(status & PHY_BMSR_LS)); + + return 0; +} + +int miiphy_print_status(struct miiphy_device *mdev) +{ + uint16_t bmsr, bmcr, anlpar; + char *duplex; + int speed; + + if (mdev->read(mdev, mdev->address, PHY_BMSR, &bmsr) != 0) + goto err_out; + if (mdev->read(mdev, mdev->address, PHY_BMCR, &bmcr) != 0) + goto err_out; + if (mdev->read(mdev, mdev->address, PHY_ANLPAR, &anlpar) != 0) + goto err_out; + + printf("%s: Link is %s", mdev->dev.id, + bmsr & PHY_BMSR_LS ? "up" : "down"); + + if (bmcr & PHY_BMCR_AUTON) { + duplex = (PHY_ANLPAR_10FD | PHY_ANLPAR_TXFD) ? "Full" : "Half"; + speed = anlpar & PHY_ANLPAR_100 ? 100 : 10; + } else { + duplex = bmcr & PHY_BMCR_DPLX ? "Full" : "Half"; + speed = bmcr & PHY_BMCR_100MB ? 100 : 10; + } + + printf(" - %d/%s\n", speed, duplex); + + return 0; +err_out: + printf("%s: failed to read\n", mdev->dev.id); + return -1; +} + +ssize_t miiphy_read(struct device_d *dev, void *_buf, size_t count, ulong offset, ulong flags) +{ + int i = count; + uint16_t *buf = _buf; + struct miiphy_device *mdev = dev->priv; + + while (i > 1) { + mdev->read(mdev, mdev->address, offset, buf); + buf++; + i -= 2; + offset++; + } + + return count; +} + +ssize_t miiphy_write(struct device_d *dev, const void *_buf, size_t count, ulong offset, ulong flags) +{ + int i = count; + const uint16_t *buf = _buf; + struct miiphy_device *mdev = dev->priv; + + while (i > 1) { + mdev->write(mdev, mdev->address, offset, *buf); + buf++; + i -= 2; + offset++; + } + + return count; +} + +static int miiphy_probe(struct device_d *dev) +{ + return 0; +} + +int miiphy_register(struct miiphy_device *mdev) +{ + strcpy(mdev->dev.name, "miiphy"); + get_free_deviceid(mdev->dev.id, "phy"); + mdev->dev.type = DEVICE_TYPE_MIIPHY; + mdev->dev.priv = mdev; + mdev->dev.size = 32; + + return register_device(&mdev->dev); +} + +static struct driver_d miiphy_drv = { + .name = "miiphy", + .probe = miiphy_probe, + .read = miiphy_read, + .write = miiphy_write, + .type = DEVICE_TYPE_MIIPHY, +}; + +static int miiphy_init(void) +{ + register_driver(&miiphy_drv); + return 0; +} + +device_initcall(miiphy_init); + diff --git a/include/mii_phy.h b/include/mii_phy.h index f0d3e62823..94adfb0a94 100644 --- a/include/mii_phy.h +++ b/include/mii_phy.h @@ -1,8 +1,99 @@ #ifndef _MII_PHY_H_ #define _MII_PHY_H_ -void mii_discover_phy(void); -unsigned short mii_phy_read(unsigned short reg); -void mii_phy_write(unsigned short reg, unsigned short val); +#define MIIPHY_FORCE_10 1 + +struct miiphy_device { + struct device_d dev; + + int address; /* The address the phy has on the bus */ + int (*read)(struct miiphy_device *mdev, uint8_t phy_addr, uint8_t reg_addr, uint16_t *value); + int (*write)(struct miiphy_device *mdev, uint8_t phy_addr, uint8_t reg_addr, uint16_t data); + + int flags; + + struct eth_device *edev; +}; + +int miiphy_register(struct miiphy_device *mdev); +int miiphy_restart_aneg(struct miiphy_device *mdev); +int miiphy_wait_aneg(struct miiphy_device *mdev); +int miiphy_print_status(struct miiphy_device *mdev); + +/* phy register offsets */ +#define PHY_BMCR 0x00 +#define PHY_BMSR 0x01 +#define PHY_PHYIDR1 0x02 +#define PHY_PHYIDR2 0x03 +#define PHY_ANAR 0x04 +#define PHY_ANLPAR 0x05 +#define PHY_ANER 0x06 +#define PHY_ANNPTR 0x07 +#define PHY_ANLPNP 0x08 +#define PHY_1000BTCR 0x09 +#define PHY_1000BTSR 0x0A +#define PHY_PHYSTS 0x10 +#define PHY_MIPSCR 0x11 +#define PHY_MIPGSR 0x12 +#define PHY_DCR 0x13 +#define PHY_FCSCR 0x14 +#define PHY_RECR 0x15 +#define PHY_PCSR 0x16 +#define PHY_LBR 0x17 +#define PHY_10BTSCR 0x18 +#define PHY_PHYCTRL 0x19 + +/* PHY BMCR */ +#define PHY_BMCR_RESET 0x8000 +#define PHY_BMCR_LOOP 0x4000 +#define PHY_BMCR_100MB 0x2000 +#define PHY_BMCR_AUTON 0x1000 +#define PHY_BMCR_POWD 0x0800 +#define PHY_BMCR_ISO 0x0400 +#define PHY_BMCR_RST_NEG 0x0200 +#define PHY_BMCR_DPLX 0x0100 +#define PHY_BMCR_COL_TST 0x0080 + +#define PHY_BMCR_SPEED_MASK 0x2040 +#define PHY_BMCR_1000_MBPS 0x0040 +#define PHY_BMCR_100_MBPS 0x2000 +#define PHY_BMCR_10_MBPS 0x0000 + +/* phy BMSR */ +#define PHY_BMSR_100T4 0x8000 +#define PHY_BMSR_100TXF 0x4000 +#define PHY_BMSR_100TXH 0x2000 +#define PHY_BMSR_10TF 0x1000 +#define PHY_BMSR_10TH 0x0800 +#define PHY_BMSR_PRE_SUP 0x0040 +#define PHY_BMSR_AUTN_COMP 0x0020 +#define PHY_BMSR_RF 0x0010 +#define PHY_BMSR_AUTN_ABLE 0x0008 +#define PHY_BMSR_LS 0x0004 +#define PHY_BMSR_JD 0x0002 +#define PHY_BMSR_EXT 0x0001 + +/*phy ANLPAR */ +#define PHY_ANLPAR_NP 0x8000 +#define PHY_ANLPAR_ACK 0x4000 +#define PHY_ANLPAR_RF 0x2000 +#define PHY_ANLPAR_T4 0x0200 +#define PHY_ANLPAR_TXFD 0x0100 +#define PHY_ANLPAR_TX 0x0080 +#define PHY_ANLPAR_10FD 0x0040 +#define PHY_ANLPAR_10 0x0020 +#define PHY_ANLPAR_100 0x0380 /* we can run at 100 */ + +#define PHY_ANLPAR_PSB_MASK 0x001f +#define PHY_ANLPAR_PSB_802_3 0x0001 +#define PHY_ANLPAR_PSB_802_9 0x0002 + +/* PHY_1000BTSR */ +#define PHY_1000BTSR_MSCF 0x8000 +#define PHY_1000BTSR_MSCR 0x4000 +#define PHY_1000BTSR_LRS 0x2000 +#define PHY_1000BTSR_RRS 0x1000 +#define PHY_1000BTSR_1000FD 0x0800 +#define PHY_1000BTSR_1000HD 0x0400 #endif -- cgit v1.2.3