changeset 1178:0832585993c1

New abstractions to handle device specifics
author gthomas
date Tue, 26 Aug 2003 17:54:00 +0000
parents 2af5a454cc1f
children 028ac74b016c
files packages/devs/eth/phy/current/ChangeLog packages/devs/eth/phy/current/cdl/phy_eth_drivers.cdl packages/devs/eth/phy/current/include/eth_phy.h packages/devs/eth/phy/current/include/eth_phy_dev.h packages/devs/eth/phy/current/src/AM79C874.c packages/devs/eth/phy/current/src/DP83847.c packages/devs/eth/phy/current/src/eth_phy.c
diffstat 7 files changed, 501 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/packages/devs/eth/phy/current/ChangeLog
+++ b/packages/devs/eth/phy/current/ChangeLog
@@ -1,3 +1,13 @@
+2003-08-26  Gary Thomas  <gary@mlbassoc.com>
+
+	* src/DP83847.c: 
+	* src/AM79C874.c: New file(s) - driver(s) for PHY devices.
+
+	* src/eth_phy.c: 
+	* include/eth_phy_dev.h: 
+	* include/eth_phy.h: 
+	* cdl/phy_eth_drivers.cdl: Add device/chip specific drivers.
+
 2003-08-19  Gary Thomas  <gary@mlbassoc.com>
 
 	* src/eth_phy.c: 
--- a/packages/devs/eth/phy/current/cdl/phy_eth_drivers.cdl
+++ b/packages/devs/eth/phy/current/cdl/phy_eth_drivers.cdl
@@ -59,4 +59,22 @@ cdl_package CYGPKG_DEVS_ETH_PHY {
     include_dir   cyg/io
 
     compile eth_phy.c
+
+    cdl_option CYGHWR_DEVS_ETH_PHY_DP83847 {
+        display       "NSDP83847"
+        flavor        bool
+        default_value 0
+        compile       -library=libextras.a DP83847.c
+        description "
+          Include support for National Semiconductor DP83847 DsPHYTER II"
+    }
+
+    cdl_option CYGHWR_DEVS_ETH_PHY_AM79C874 {
+        display       "AMD 79C874"
+        flavor        bool
+        default_value 0
+        compile       -library=libextras.a AM79C874.c
+        description "
+          Include support for AMD 79C874 NetPHY"
+    }
 }
--- a/packages/devs/eth/phy/current/include/eth_phy.h
+++ b/packages/devs/eth/phy/current/include/eth_phy.h
@@ -4,7 +4,7 @@
 //
 //      eth_phy.h
 //
-//      API for ethernet transciever (PHY) support
+//      User API for ethernet transciever (PHY) support
 //
 //==========================================================================
 //####ECOSGPLCOPYRIGHTBEGIN####
@@ -52,36 +52,48 @@
 //
 //==========================================================================
 
-
-// Transceiver mode
-#define PHY_BMCR             0x00    // Register number
-#define PHY_BMCR_RESET       0x8000
-#define PHY_BMCR_LOOPBACK    0x4000
-#define PHY_BMCR_100MB       0x2000
-#define PHY_BMCR_AUTO_NEG    0x1000
-#define PHY_BMCR_POWER_DOWN  0x0800
-#define PHY_BMCR_ISOLATE     0x0400
-#define PHY_BMCR_RESTART     0x0200
-#define PHY_BMCR_FULL_DUPLEX 0x0100
-#define PHY_BMCR_COLL_TEST   0x0080
-
-#define PHY_BMSR             0x01    // Status register
-#define PHY_BMSR_AUTO_NEG    0x0020  
-#define PHY_BMSR_LINK        0x0004
+#define PHY_BIT_LEVEL_ACCESS_TYPE 0
+#define PHY_REG_LEVEL_ACCESS_TYPE 1
 
 // Physical device access - defined by hardware instance
 typedef struct {
+    int ops_type;  // 0 => bit level, 1 => register level
+    bool init_done;
     void (*init)(void);
-    void (*set_data)(int);
-    int  (*get_data)(void);
-    void (*set_clock)(int);
-    void (*set_dir)(int);
+    void (*reset)(void);
+    union {
+        struct {
+            void (*set_data)(int);
+            int  (*get_data)(void);
+            void (*set_clock)(int);
+            void (*set_dir)(int);
+        } bit_level_ops;
+        struct {
+            void (*put_reg)(int reg, int unit, unsigned short data);
+            bool (*get_reg)(int reg, int unit, unsigned short *data);
+        } reg_level_ops;
+    } ops;
+    int phy_addr;
+    struct _eth_phy_dev_entry *dev;  // Chip access functions
 } eth_phy_access_t;
 
-#define ETH_PHY_ACCESS_FUNS(_l,_init,_set_data,_get_data,_set_clock,_set_dir) \
-static eth_phy_access_t _l = {_init, _set_data, _get_data, _set_clock, _set_dir}
+#define ETH_PHY_BIT_LEVEL_ACCESS_FUNS(_l,_init,_reset,_set_data,_get_data,_set_clock,_set_dir) \
+static eth_phy_access_t _l = {PHY_BIT_LEVEL_ACCESS_TYPE, false, _init, _reset, \
+                              {.bit_level_ops = {_set_data, _get_data, _set_clock, _set_dir}}}
+
+#define ETH_PHY_REG_LEVEL_ACCESS_FUNS(_l,_init,_reset,_put_reg,_get_reg) \
+static eth_phy_access_t _l = {PHY_REG_LEVEL_ACCESS_TYPE, false, _init, _reset, \
+                              {.reg_level_ops = {_put_reg, _get_reg}}}
 
-externC void _eth_phy_init(eth_phy_access_t *f);
+#define ETH_PHY_STAT_LINK  0x0001   // Link up/down
+#define ETH_PHY_STAT_100MB 0x0002   // Connection is 100Mb
+#define ETH_PHY_STAT_FDX   0x0004   // Connection is full duplex
+
+externC bool _eth_phy_init(eth_phy_access_t *f);
+externC void _eth_phy_reset(eth_phy_access_t *f);
+externC int  _eth_phy_state(eth_phy_access_t *f);
+externC int  _eth_phy_cfg(eth_phy_access_t *f, int mode);
+// Internal routines
 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);
 
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/phy/current/include/eth_phy_dev.h
@@ -0,0 +1,91 @@
+#ifndef CYGONCE_DEVS_ETH_PHY_DEV_H_
+#define CYGONCE_DEVS_ETH_PHY_DEV_H_
+//==========================================================================
+//
+//      eth_phy_dev.h
+//
+//      Device API for ethernet transciever (PHY) support
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Gary Thomas
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas
+// Date:         2003-08-01
+// Purpose:      
+// Description:  
+//              
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+// Transceiver mode
+#define PHY_BMCR             0x00    // Register number
+#define PHY_BMCR_RESET       0x8000
+#define PHY_BMCR_LOOPBACK    0x4000
+#define PHY_BMCR_100MB       0x2000
+#define PHY_BMCR_AUTO_NEG    0x1000
+#define PHY_BMCR_POWER_DOWN  0x0800
+#define PHY_BMCR_ISOLATE     0x0400
+#define PHY_BMCR_RESTART     0x0200
+#define PHY_BMCR_FULL_DUPLEX 0x0100
+#define PHY_BMCR_COLL_TEST   0x0080
+
+#define PHY_BMSR             0x01    // Status register
+#define PHY_BMSR_100T4       0x8000
+#define PHY_BMSR_100FDX      0x4000
+#define PHY_BMSR_100HDX      0x2000
+#define PHY_BMSR_10FDX       0x1000
+#define PHY_BMSR_10HDX       0x0800
+#define PHY_BMSR_AUTO_NEG    0x0020  
+#define PHY_BMSR_LINK        0x0004
+
+#define PHY_ID1              0x02    // Chip ID register (high 16 bits)
+#define PHY_ID2              0x03    // Chip ID register (low 16 bits)
+
+struct _eth_phy_dev_entry {
+    char          *name;
+    unsigned long  id;
+    bool         (*stat)(eth_phy_access_t *f, int *stat);
+} CYG_HAL_TABLE_TYPE;
+
+#define _eth_phy_dev(_name_,_id_,_stat_)                \
+struct _eth_phy_dev_entry _eth_phy_dev_##_id_           \
+   CYG_HAL_TABLE_QUALIFIED_ENTRY(_eth_phy_devs,_id_) =  \
+     { _name_, _id_, _stat_ }; 
+
+#endif  // CYGONCE_DEVS_ETH_PHY_DEV_H_
+// ------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/phy/current/src/AM79C874.c
@@ -0,0 +1,101 @@
+//==========================================================================
+//
+//      dev/AM79C874.c
+//
+//      Ethernet transciever (PHY) support 
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Gary Thomas
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: 
+// Date:         2003-08-01
+// Purpose:      
+// Description:  Support for ethernet NS AM79C874 PHY
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/diag.h>
+
+#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>
+
+static bool am79c874_stat(eth_phy_access_t *f, int *state)
+{
+    unsigned short phy_state;
+    int tries;
+
+    // Read negotiated state
+    if (_eth_phy_read(f, 0x1, f->phy_addr, &phy_state)) {
+        if ((phy_state & 0x20) == 0) {
+            diag_printf("... waiting for auto-negotiation");
+            for (tries = 0;  tries < 15;  tries++) {
+                if (_eth_phy_read(f, 0x1, f->phy_addr, &phy_state)) {
+                    if ((phy_state & 0x20) != 0) {
+                        break;
+                    }
+                }
+                CYGACC_CALL_IF_DELAY_US(1000000);   // 1 second
+                diag_printf(".");
+            }
+            diag_printf("\n");
+        }
+        if ((phy_state & 0x20) != 0) {
+            *state = 0;
+            if ((phy_state & 0x0004) != 0) *state |= ETH_PHY_STAT_LINK;
+            if (_eth_phy_read(f, 0x5, f->phy_addr, &phy_state)) {
+                // Partner negotiated parameters
+                if ((phy_state & 0x0100) != 0) *state |= ETH_PHY_STAT_100MB | ETH_PHY_STAT_FDX;
+                if ((phy_state & 0x0080) != 0) *state |= ETH_PHY_STAT_100MB;
+                if ((phy_state & 0x0040) != 0) *state |= ETH_PHY_STAT_FDX;
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+_eth_phy_dev("AMD AM79C874", 0x0022561B, am79c874_stat)
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/phy/current/src/DP83847.c
@@ -0,0 +1,97 @@
+//==========================================================================
+//
+//      dev/DP83847.c
+//
+//      Ethernet transciever (PHY) support 
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Gary Thomas
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: 
+// Date:         2003-08-01
+// Purpose:      
+// Description:  Support for ethernet NS DP83847 PHY
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/diag.h>
+
+#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>
+
+static bool dp83847_stat(eth_phy_access_t *f, int *state)
+{
+    unsigned short phy_state;
+    int tries;
+
+    // Read negotiated state
+    if (_eth_phy_read(f, 0x10, f->phy_addr, &phy_state)) {
+        if ((phy_state & 0x10) == 0) {
+            diag_printf("... waiting for auto-negotiation");
+            for (tries = 0;  tries < 15;  tries++) {
+                if (_eth_phy_read(f, 0x10, f->phy_addr, &phy_state)) {
+                    if ((phy_state & 0x10) != 0) {
+                        break;
+                    }
+                }
+                CYGACC_CALL_IF_DELAY_US(1000000);   // 1 second
+                diag_printf(".");
+            }
+            diag_printf("\n");
+        }
+        if ((phy_state & 0x10) != 0) {
+            *state = 0;
+            if ((phy_state & 0x0001) != 0) *state |= ETH_PHY_STAT_LINK;
+            if ((phy_state & 0x0002) == 0) *state |= ETH_PHY_STAT_100MB;
+            if ((phy_state & 0x0004) != 0) *state |= ETH_PHY_STAT_FDX;
+            return true;
+        }
+    }
+    return false;
+}
+
+_eth_phy_dev("National Semiconductor DP83847", 0x20005c30, dp83847_stat)
--- 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;
+}