summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/Kconfig4
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/miiphy.c167
-rw-r--r--include/mii_phy.h97
4 files changed, 266 insertions, 3 deletions
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 <common.h>
+#include <driver.h>
+#include <init.h>
+#include <mii_phy.h>
+
+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