From 9b2aabe534eb3804130ca0861cb23c1479cd7e02 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 18 Apr 2012 16:30:11 +0100 Subject: [PATCH] [mii] Add generic MII reset function iPXE provides no support for manually configuring the link speed. Provide a generic routine which should be able to reset any MII/GMII PHY and enable autonegotiation. Prototyped-by: Thomas Miletich Signed-off-by: Michael Brown --- src/drivers/net/mii.c | 85 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/mii.h | 86 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 src/drivers/net/mii.c create mode 100644 src/include/ipxe/mii.h diff --git a/src/drivers/net/mii.c b/src/drivers/net/mii.c new file mode 100644 index 00000000..c8e6529d --- /dev/null +++ b/src/drivers/net/mii.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program 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 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include + +/** @file + * + * Media Independent Interface + * + */ + +/** + * Reset MII interface + * + * @v mii MII interface + * @ret rc Return status code + */ +int mii_reset ( struct mii_interface *mii ) { + unsigned int i; + int bmcr; + int rc; + + /* Power-up, enable autonegotiation and initiate reset */ + if ( ( rc = mii_write ( mii, MII_BMCR, + ( BMCR_RESET | BMCR_ANENABLE ) ) ) != 0 ) { + DBGC ( mii, "MII %p could not write BMCR: %s\n", + mii, strerror ( rc ) ); + return rc; + } + + /* Wait for reset to complete */ + for ( i = 0 ; i < MII_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset has completed */ + bmcr = mii_read ( mii, MII_BMCR ); + if ( bmcr < 0 ) { + rc = bmcr; + DBGC ( mii, "MII %p could not read BMCR: %s\n", + mii, strerror ( rc ) ); + return rc; + } + + /* If reset is not complete, delay 1ms and retry */ + if ( bmcr & BMCR_RESET ) { + mdelay ( 1 ); + continue; + } + + /* Force autonegotation on again, in case it was + * cleared by the reset. + */ + if ( ( rc = mii_write ( mii, MII_BMCR, BMCR_ANENABLE ) ) != 0 ){ + DBGC ( mii, "MII %p could not write BMCR: %s\n", + mii, strerror ( rc ) ); + return rc; + } + + DBGC ( mii, "MII %p reset after %dms\n", mii, i ); + return 0; + } + + DBGC ( mii, "MII %p timed out waiting for reset\n", mii ); + return -ETIMEDOUT; +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 77c2f809..87b5ac72 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -141,6 +141,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ath9k ( ERRFILE_DRIVER | 0x005f0000 ) #define ERRFILE_ath ( ERRFILE_DRIVER | 0x00600000 ) #define ERRFILE_vmxnet3 ( ERRFILE_DRIVER | 0x00610000 ) +#define ERRFILE_mii ( ERRFILE_DRIVER | 0x00620000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) diff --git a/src/include/ipxe/mii.h b/src/include/ipxe/mii.h new file mode 100644 index 00000000..a33a31d9 --- /dev/null +++ b/src/include/ipxe/mii.h @@ -0,0 +1,86 @@ +#ifndef _IPXE_MII_H +#define _IPXE_MII_H + +/** @file + * + * Media Independent Interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +struct mii_interface; + +/** MII interface operations */ +struct mii_operations { + /** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret data Data read, or negative error + */ + int ( * read ) ( struct mii_interface *mii, unsigned int reg ); + /** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ + int ( * write ) ( struct mii_interface *mii, unsigned int reg, + unsigned int data ); +}; + +/** An MII interface */ +struct mii_interface { + /** Interface operations */ + struct mii_operations *op; +}; + +/** + * Initialise MII interface + * + * @v mii MII interface + * @v op MII interface operations + */ +static inline __attribute__ (( always_inline )) void +mii_init ( struct mii_interface *mii, struct mii_operations *op ) { + mii->op = op; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret data Data read, or negative error + */ +static inline __attribute__ (( always_inline )) int +mii_read ( struct mii_interface *mii, unsigned int reg ) { + return mii->op->read ( mii, reg ); +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +mii_write ( struct mii_interface *mii, unsigned int reg, unsigned int data ) { + return mii->op->write ( mii, reg, data ); +} + +/** Maximum time to wait for a reset, in milliseconds */ +#define MII_RESET_MAX_WAIT_MS 500 + +extern int mii_reset ( struct mii_interface *mii ); + +#endif /* _IPXE_MII_H */