diff --git a/arch/arm/configs/freescale_mx53_loco_defconfig b/arch/arm/configs/freescale_mx53_loco_defconfig index 37aa182..b50b050 100644 --- a/arch/arm/configs/freescale_mx53_loco_defconfig +++ b/arch/arm/configs/freescale_mx53_loco_defconfig @@ -53,6 +53,7 @@ CONFIG_CMD_TFTP=y CONFIG_FS_TFTP=y CONFIG_NET_NETCONSOLE=y +CONFIG_SMSC_PHY=y CONFIG_DRIVER_NET_FEC_IMX=y # CONFIG_SPI is not set CONFIG_I2C=y diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 21ce38c..de4bd6e 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -370,26 +370,6 @@ } } -static int fec_phy_connect(struct eth_device *edev) -{ - struct fec_priv *fec = (struct fec_priv *)edev->priv; - int ret; - - if (fec->xcv_type == SEVENWIRE) - return 0; - - ret = phy_device_connect(edev, &fec->miibus, fec->phy_addr, - fec_update_linkspeed, fec->phy_flags, - fec->interface); - if (ret) - return ret; - - if (fec->phy_init) - fec->phy_init(edev->phydev); - - return 0; -} - /** * Start the FEC engine * @param[in] edev Our device to handle @@ -397,8 +377,20 @@ static int fec_open(struct eth_device *edev) { struct fec_priv *fec = (struct fec_priv *)edev->priv; + int ret; u32 ecr; + if (fec->xcv_type != SEVENWIRE) { + ret = phy_device_connect(edev, &fec->miibus, fec->phy_addr, + fec_update_linkspeed, fec->phy_flags, + fec->interface); + if (ret) + return ret; + + if (fec->phy_init) + fec->phy_init(edev->phydev); + } + /* * Initialize RxBD/TxBD rings */ @@ -713,18 +705,9 @@ mdiobus_register(&fec->miibus); } - ret = eth_register(edev); - if (ret) - goto err_free; - - ret = fec_phy_connect(edev); - if (ret) - goto err_unregister; - + eth_register(edev); return 0; -err_unregister: - eth_unregister(edev); err_free: free(fec); return ret; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index b66261a..616b539 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -14,4 +14,9 @@ endif +config SMSC_PHY + bool "Drivers for SMSC PHYs" + ---help--- + Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs + endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 82e90d4..0caeb70 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,2 +1,3 @@ obj-y += phy.o mdio_bus.o obj-$(CONFIG_GENERIC_PHY) += generic.o +obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 88c3ff7..e1ecc27 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -581,3 +581,15 @@ return register_driver(&phydrv->drv); } + +int phy_drivers_register(struct phy_driver *new_driver, int n) +{ + int i, ret = 0; + + for (i = 0; i < n; i++) { + ret = phy_driver_register(new_driver + i); + if (ret) + return ret; + } + return ret; +} diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c new file mode 100644 index 0000000..a318624 --- /dev/null +++ b/drivers/net/phy/smsc.c @@ -0,0 +1,146 @@ +/* + * drivers/net/phy/smsc.c + * + * Driver for SMSC PHYs + * + * Author: Herbert Valerio Riedel + * + * Copyright (c) 2006 Herbert Valerio Riedel + * + * 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. + * + * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net + * + */ + +#include +#include +#include +#include +#include +#include + +static int smsc_phy_ack_interrupt(struct phy_device *phydev) +{ + int rc = phy_read (phydev, MII_LAN83C185_ISF); + + return rc < 0 ? rc : 0; +} + +static int smsc_phy_config_init(struct phy_device *phydev) +{ + int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + + /* Enable energy detect mode for this SMSC Transceivers */ + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, + rc | MII_LAN83C185_EDPWRDOWN); + if (rc < 0) + return rc; + + return smsc_phy_ack_interrupt (phydev); +} + +static int lan87xx_config_init(struct phy_device *phydev) +{ + /* + * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on + * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due + * to a bug on the chip. + * + * When the system is powered on with the network cable being + * disconnected all the way until after ifconfig ethX up is + * issued for the LAN port with this PHY, connecting the cable + * afterwards does not cause LINK change detection, while the + * expected behavior is the Link UP being detected. + */ + int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + + rc &= ~MII_LAN83C185_EDPWRDOWN; + + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc); + if (rc < 0) + return rc; + + return smsc_phy_ack_interrupt(phydev); +} + +static int lan911x_config_init(struct phy_device *phydev) +{ + return smsc_phy_ack_interrupt(phydev); +} + +static struct phy_driver smsc_phy_driver[] = { +{ + .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ + .phy_id_mask = 0xfffffff0, + .drv.name = "SMSC LAN83C185", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, +}, { + .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ + .phy_id_mask = 0xfffffff0, + .drv.name = "SMSC LAN8187", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, +}, { + .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ + .phy_id_mask = 0xfffffff0, + .drv.name = "SMSC LAN8700", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, +}, { + .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ + .phy_id_mask = 0xfffffff0, + .drv.name = "SMSC LAN911x Internal PHY", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = lan911x_config_init, +}, { + .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ + .phy_id_mask = 0xfffffff0, + .drv.name = "SMSC LAN8710/LAN8720", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = lan87xx_config_init, +} }; + +static int __init smsc_init(void) +{ + return phy_drivers_register(smsc_phy_driver, ARRAY_SIZE(smsc_phy_driver)); +} +fs_initcall(smsc_init); diff --git a/include/linux/phy.h b/include/linux/phy.h index 76f9edb..4f14dae 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -226,6 +226,7 @@ #define PHY_ANY_UID 0xffffffff int phy_driver_register(struct phy_driver *drv); +int phy_drivers_register(struct phy_driver *new_driver, int n); struct phy_device *get_phy_device(struct mii_bus *bus, int addr); int phy_init(void); diff --git a/include/linux/smscphy.h b/include/linux/smscphy.h new file mode 100644 index 0000000..ce718cb --- /dev/null +++ b/include/linux/smscphy.h @@ -0,0 +1,25 @@ +#ifndef __LINUX_SMSCPHY_H__ +#define __LINUX_SMSCPHY_H__ + +#define MII_LAN83C185_ISF 29 /* Interrupt Source Flags */ +#define MII_LAN83C185_IM 30 /* Interrupt Mask */ +#define MII_LAN83C185_CTRL_STATUS 17 /* Mode/Status Register */ + +#define MII_LAN83C185_ISF_INT1 (1<<1) /* Auto-Negotiation Page Received */ +#define MII_LAN83C185_ISF_INT2 (1<<2) /* Parallel Detection Fault */ +#define MII_LAN83C185_ISF_INT3 (1<<3) /* Auto-Negotiation LP Ack */ +#define MII_LAN83C185_ISF_INT4 (1<<4) /* Link Down */ +#define MII_LAN83C185_ISF_INT5 (1<<5) /* Remote Fault Detected */ +#define MII_LAN83C185_ISF_INT6 (1<<6) /* Auto-Negotiation complete */ +#define MII_LAN83C185_ISF_INT7 (1<<7) /* ENERGYON */ + +#define MII_LAN83C185_ISF_INT_ALL (0x0e) + +#define MII_LAN83C185_ISF_INT_PHYLIB_EVENTS \ + (MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4 | \ + MII_LAN83C185_ISF_INT7) + +#define MII_LAN83C185_EDPWRDOWN (1 << 13) /* EDPWRDOWN */ +#define MII_LAN83C185_ENERGYON (1 << 1) /* ENERGYON */ + +#endif /* __LINUX_SMSCPHY_H__ */