diff packages/devs/eth/phy/current/src/eth_phy.c @ 1178:0832585993c1

New abstractions to handle device specifics
author gthomas
date Tue, 26 Aug 2003 17:54:00 +0000
parents 719dd033d039
children 0418d60dd8fb
line wrap: on
line diff
--- a/packages/devs/eth/phy/current/src/eth_phy.c
+++ b/packages/devs/eth/phy/current/src/eth_phy.c
@@ -58,8 +58,15 @@
 #include <cyg/hal/hal_arch.h>
 #include <cyg/hal/drv_api.h>
 #include <cyg/hal/hal_if.h>
+#include <cyg/hal/hal_tables.h>
 
 #include <cyg/io/eth_phy.h>
+#include <cyg/io/eth_phy_dev.h>
+
+// Define table boundaries
+CYG_HAL_TABLE_BEGIN( __ETH_PHY_TAB__, _eth_phy_devs );
+CYG_HAL_TABLE_END( __ETH_PHY_TAB_END__, _eth_phy_devs );
+extern struct _eth_phy_dev_entry __ETH_PHY_TAB__[], __ETH_PHY_TAB_END__;
 
 // MII interface
 #define MII_Start            0x40000000
@@ -71,7 +78,7 @@
 #define MII_TA               0x00020000
 
 //
-// PHY unit access (via MII channel)
+// PHY unit access (via MII channel, using bit-level operations)
 //
 
 static cyg_uint32
@@ -82,23 +89,23 @@ phy_cmd(eth_phy_access_t *f, cyg_uint32 
     bool        is_read = ((cmd & MII_Cmd) == MII_Read);
 
     // Set both bits as output
-    (f->set_dir)(1);
+    (f->ops.bit_level_ops.set_dir)(1);
 
     // Preamble
     for (i = 0; i < 32; i++) {
-        (f->set_clock)(0);
-        (f->set_data)(1);
+        (f->ops.bit_level_ops.set_clock)(0);
+        (f->ops.bit_level_ops.set_data)(1);
         CYGACC_CALL_IF_DELAY_US(1);
-        (f->set_clock)(1);
+        (f->ops.bit_level_ops.set_clock)(1);
         CYGACC_CALL_IF_DELAY_US(1);
     }
 
     // Command/data
     for (i = 0, off = 31; i < (is_read ? 14 : 32); i++, --off) {
-        (f->set_clock)(0);
-        (f->set_data)((cmd >> off) & 0x00000001);
+        (f->ops.bit_level_ops.set_clock)(0);
+        (f->ops.bit_level_ops.set_data)((cmd >> off) & 0x00000001);
         CYGACC_CALL_IF_DELAY_US(1);
-        (f->set_clock)(1);
+        (f->ops.bit_level_ops.set_clock)(1);
         CYGACC_CALL_IF_DELAY_US(1);
     }
 
@@ -108,49 +115,95 @@ phy_cmd(eth_phy_access_t *f, cyg_uint32 
     if (is_read) {
         retval >>= 16;
 
-        (f->set_clock)(0);
-        (f->set_dir)(0);
+        (f->ops.bit_level_ops.set_clock)(0);
+        (f->ops.bit_level_ops.set_dir)(0);
         CYGACC_CALL_IF_DELAY_US(1);
-        (f->set_clock)(1);
+        (f->ops.bit_level_ops.set_clock)(1);
         CYGACC_CALL_IF_DELAY_US(1);
-        (f->set_clock)(0);
+        (f->ops.bit_level_ops.set_clock)(0);
         CYGACC_CALL_IF_DELAY_US(1);
 
         for (i = 0, off = 15; i < 16; i++, off--) {
-            (f->set_clock)(1);
+            (f->ops.bit_level_ops.set_clock)(1);
             retval <<= 1;
-            retval |= (f->get_data)();
+            retval |= (f->ops.bit_level_ops.get_data)();
             CYGACC_CALL_IF_DELAY_US(1);
-            (f->set_clock)(0);
+            (f->ops.bit_level_ops.set_clock)(0);
             CYGACC_CALL_IF_DELAY_US(1);
         }
     }
 
     // Set both bits as output
-    (f->set_dir)(1);
+    (f->ops.bit_level_ops.set_dir)(1);
 
     // Postamble
     for (i = 0; i < 32; i++) {
-        (f->set_clock)(0);
-        (f->set_data)(1);
+        (f->ops.bit_level_ops.set_clock)(0);
+        (f->ops.bit_level_ops.set_data)(1);
         CYGACC_CALL_IF_DELAY_US(1);
-        (f->set_clock)(1);
+        (f->ops.bit_level_ops.set_clock)(1);
         CYGACC_CALL_IF_DELAY_US(1);
     }
 
     return retval;
 }
 
-externC void
+externC bool
 _eth_phy_init(eth_phy_access_t *f)
 {
+    int addr;
+    unsigned short state;
+    unsigned long id;
+    struct _eth_phy_dev_entry *dev;
+
+    if (f->init_done) return true;
+    (f->init)();
+    // Scan to determine PHY address
+    f->init_done = true;
+    for (addr = 0;  addr < 0x20;  addr++) {
+        if (_eth_phy_read(f, PHY_ID1, addr, &state)) {
+            id = state << 16;
+            if (_eth_phy_read(f, PHY_ID2, addr, &state)) {
+                id |= state;
+                f->phy_addr = addr;
+                for (dev = __ETH_PHY_TAB__; dev != &__ETH_PHY_TAB_END__;  dev++) {
+                    if (dev->id == id) {
+                        diag_printf("PHY: %s\n", dev->name);
+                        f->dev = dev;
+                        return true;
+                    }
+                }
+                diag_printf("Unsupported PHY device - id: %x\n", id);
+                break;  // Can't handle this PHY
+            }
+        }
+    }
+    f->init_done = false;
+    return false;
+}
+
+externC void
+_eth_phy_reset(eth_phy_access_t *f)
+{
+    if (!f->init_done) {
+        diag_printf("PHY reset without init on PHY: %x\n", f);
+        return;
+    }
     (f->init)();
 }
 
 externC void
 _eth_phy_write(eth_phy_access_t *f, int reg, int addr, unsigned short data)
 {
-    phy_cmd(f, MII_Start | MII_Write | MII_Phy(addr) | MII_Reg(reg) | MII_TA | data);
+    if (!f->init_done) {
+        diag_printf("PHY write without init on PHY: %x\n", f);
+        return;
+    }
+    if (f->ops_type == PHY_BIT_LEVEL_ACCESS_TYPE) {
+        phy_cmd(f, MII_Start | MII_Write | MII_Phy(addr) | MII_Reg(reg) | MII_TA | data);
+    } else {
+        (f->ops.reg_level_ops.put_reg)(reg, addr, data);
+    }
 }
 
 externC bool
@@ -158,11 +211,79 @@ externC bool
 {
     cyg_uint32 ret;
 
-    ret = phy_cmd(f, MII_Start | MII_Read | MII_Phy(addr) | MII_Reg(reg) | MII_TA);
-    *val = ret;
-    return true;
+    if (!f->init_done) {
+        diag_printf("PHY read without init on PHY: %x\n", f);
+        return false;
+    }
+    if (f->ops_type == PHY_BIT_LEVEL_ACCESS_TYPE) {
+        ret = phy_cmd(f, MII_Start | MII_Read | MII_Phy(addr) | MII_Reg(reg) | MII_TA);
+        *val = ret;
+        return true;
+    } else {
+        return (f->ops.reg_level_ops.get_reg)(reg, addr, val);
+    }
 }
 
-externC void _eth_phy_init(eth_phy_access_t *f);
-externC void _eth_phy_write(eth_phy_access_t *f, int reg, int unit, unsigned short data);
-externC bool _eth_phy_read(eth_phy_access_t *f, int reg, int unit, unsigned short *val);
+externC int
+_eth_phy_cfg(eth_phy_access_t *f, int mode)
+{
+    int phy_timeout = 5*1000;  // Wait 5 seconds max for link to clear
+    bool phy_ok;
+    unsigned short reset_mode, phy_state;
+    int i;
+
+    if (!f->init_done) {
+        diag_printf("PHY config without init on PHY: %x\n", f);
+        return;
+    }
+
+    // Reset PHY (transceiver)
+    phy_ok = false;
+    _eth_phy_reset(f);
+
+    _eth_phy_write(f, PHY_BMCR, f->phy_addr, PHY_BMCR_RESET);
+    for (i = 0;  i < 5*100;  i++) {
+        phy_ok = _eth_phy_read(f, PHY_BMCR, f->phy_addr, &phy_state);            
+        diag_printf("PHY: %x\n", phy_state);
+        if (phy_ok && !(phy_state & PHY_BMCR_RESET)) break;
+        CYGACC_CALL_IF_DELAY_US(10000);   // 10ms
+    }
+    if (!phy_ok || (phy_state & PHY_BMCR_RESET)) {
+        diag_printf("PPC405: Can't get PHY unit to soft reset: %x\n", phy_state);
+        return 0;
+    }
+
+    reset_mode = PHY_BMCR_RESTART | PHY_BMCR_AUTO_NEG;
+    _eth_phy_write(f, PHY_BMCR, f->phy_addr, reset_mode);
+    while (phy_timeout-- >= 0) {
+        phy_ok = _eth_phy_read(f, PHY_BMSR, f->phy_addr, &phy_state);
+        if (phy_ok && (phy_state & PHY_BMSR_AUTO_NEG)) {
+            break;
+        } else {
+            CYGACC_CALL_IF_DELAY_US(10000);   // 10ms
+        }
+    }
+    if (phy_timeout <= 0) {
+        diag_printf("** PPC405 Warning: PHY LINK UP failed: %04x\n", phy_state);
+        return 0;
+    }
+
+    return _eth_phy_state(f);
+}
+
+externC int
+_eth_phy_state(eth_phy_access_t *f)
+{
+    int state = 0;
+
+    if (!f->init_done) {
+        diag_printf("PHY state without init on PHY: %x\n", f);
+        return 0;
+    }
+    if ((f->dev->stat)(f, &state)) {
+        return state;
+    } else {
+        return 0;
+    }
+    return state;
+}