eth_phy.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 294 行
C
294 行
//==========================================================================
//
// dev/eth_phy.c
//
// Ethernet transciever (PHY) support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2003, 2004 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: API support for ethernet PHY
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/io_eth_drivers.h>
#include <pkgconf/devs_eth_phy.h>
#include <cyg/infra/cyg_type.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>
// 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
#define MII_Read 0x20000000
#define MII_Write 0x10000000
#define MII_Cmd 0x30000000
#define MII_Phy(phy) (phy << 23)
#define MII_Reg(reg) (reg << 18)
#define MII_TA 0x00020000
//
// PHY unit access (via MII channel, using bit-level operations)
//
static cyg_uint32
phy_cmd(eth_phy_access_t *f, cyg_uint32 cmd)
{
cyg_uint32 retval;
int i, off;
bool is_read = ((cmd & MII_Cmd) == MII_Read);
// Set both bits as output
(f->ops.bit_level_ops.set_dir)(1);
// Preamble
for (i = 0; i < 32; i++) {
(f->ops.bit_level_ops.set_clock)(0);
(f->ops.bit_level_ops.set_data)(1);
CYGACC_CALL_IF_DELAY_US(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->ops.bit_level_ops.set_clock)(0);
(f->ops.bit_level_ops.set_data)((cmd >> off) & 0x00000001);
CYGACC_CALL_IF_DELAY_US(1);
(f->ops.bit_level_ops.set_clock)(1);
CYGACC_CALL_IF_DELAY_US(1);
}
retval = cmd;
// If read, fetch data register
if (is_read) {
retval >>= 16;
(f->ops.bit_level_ops.set_clock)(0);
(f->ops.bit_level_ops.set_dir)(0);
CYGACC_CALL_IF_DELAY_US(1);
(f->ops.bit_level_ops.set_clock)(1);
CYGACC_CALL_IF_DELAY_US(1);
(f->ops.bit_level_ops.set_clock)(0);
CYGACC_CALL_IF_DELAY_US(1);
for (i = 0, off = 15; i < 16; i++, off--) {
(f->ops.bit_level_ops.set_clock)(1);
retval <<= 1;
retval |= (f->ops.bit_level_ops.get_data)();
CYGACC_CALL_IF_DELAY_US(1);
(f->ops.bit_level_ops.set_clock)(0);
CYGACC_CALL_IF_DELAY_US(1);
}
}
// Set both bits as output
(f->ops.bit_level_ops.set_dir)(1);
// Postamble
for (i = 0; i < 32; i++) {
(f->ops.bit_level_ops.set_clock)(0);
(f->ops.bit_level_ops.set_data)(1);
CYGACC_CALL_IF_DELAY_US(1);
(f->ops.bit_level_ops.set_clock)(1);
CYGACC_CALL_IF_DELAY_US(1);
}
return retval;
}
externC bool
_eth_phy_init(eth_phy_access_t *f)
{
int addr;
unsigned short state;
unsigned long id = 0;
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) {
eth_phy_printf("PHY: %s\n", dev->name);
f->dev = dev;
return true;
}
}
}
}
}
if (addr >= 0x20)
{
// Can't handle this PHY
eth_phy_printf("Unsupported PHY device - id: %x\n", id);
}
f->init_done = false;
return false;
}
externC void
_eth_phy_reset(eth_phy_access_t *f)
{
if (!f->init_done) {
eth_phy_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)
{
if (!f->init_done) {
eth_phy_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
_eth_phy_read(eth_phy_access_t *f, int reg, int addr, unsigned short *val)
{
cyg_uint32 ret;
if (!f->init_done) {
eth_phy_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 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) {
eth_phy_printf("PHY config without init on PHY: %x\n", f);
return 0;
}
// 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);
eth_phy_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)) {
eth_phy_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) {
eth_phy_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) {
eth_phy_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;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?