From e7d143f036ee9a0bddb1492a587d9d91e092b907 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 16 Nov 2012 18:19:51 +0100 Subject: [PATCH 1/9] phylib: export phy_id via param Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 35319b430..b4720872a 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -193,6 +193,9 @@ static int mdio_bus_probe(struct device_d *_dev) sprintf(str, "%d", dev->addr); dev_add_param_fixed(&dev->dev, "phy_addr", str); + sprintf(str, "0x%08x", dev->phy_id); + dev_add_param_fixed(&dev->dev, "phy_id", str); + dev->cdev.name = asprintf("phy%d", _dev->id); dev->cdev.size = 64; dev->cdev.ops = &phydev_ops; From d1662f9db3d3ba743bd73c72cca86f71f86c2ad4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 16 Nov 2012 18:19:52 +0100 Subject: [PATCH 2/9] phylib: introduction of forced 100Mbps Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 4 ++++ include/linux/phy.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b4720872a..544d743d9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -175,6 +175,10 @@ static int mdio_bus_probe(struct device_d *_dev) dev->speed = SPEED_10; dev->duplex = DUPLEX_FULL; dev->autoneg = !AUTONEG_ENABLE; + } else if (dev->dev_flags & PHYLIB_FORCE_100) { + dev->speed = SPEED_100; + dev->duplex = DUPLEX_FULL; + dev->autoneg = !AUTONEG_ENABLE; } } diff --git a/include/linux/phy.h b/include/linux/phy.h index 4f14daef6..791d657c9 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -60,7 +60,8 @@ typedef enum { #define MII_BUS_ID_SIZE (20 - 3) #define PHYLIB_FORCE_10 (1 << 0) -#define PHYLIB_FORCE_LINK (1 << 1) +#define PHYLIB_FORCE_100 (1 << 1) +#define PHYLIB_FORCE_LINK (1 << 2) #define PHYLIB_CAPABLE_1000M (1 << 0) From c81154671253d730ad159db136235bfe2111d2db Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 16 Nov 2012 18:19:53 +0100 Subject: [PATCH 3/9] macb/ether: split flags for drivers and phylib as in the kernel use is_rmii flags for pinctrl phy_flags for phylib flags Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- arch/arm/boards/at91rm9200ek/init.c | 2 +- arch/arm/boards/at91sam9260ek/init.c | 2 +- arch/arm/boards/at91sam9263ek/init.c | 2 +- arch/arm/boards/at91sam9m10g45ek/init.c | 2 +- arch/arm/boards/at91sam9x5ek/init.c | 2 +- arch/arm/boards/mmccpu/init.c | 2 +- arch/arm/boards/pm9263/init.c | 2 +- arch/arm/boards/pm9g45/init.c | 2 +- arch/arm/boards/qil-a9260/init.c | 2 +- arch/arm/boards/tny-a926x/init.c | 2 +- arch/arm/boards/usb-a926x/init.c | 2 +- arch/arm/mach-at91/at91rm9200_devices.c | 2 +- arch/arm/mach-at91/at91sam9260_devices.c | 2 +- arch/arm/mach-at91/at91sam9263_devices.c | 2 +- arch/arm/mach-at91/at91sam9g45_devices.c | 2 +- arch/arm/mach-at91/at91sam9x5_devices.c | 4 ++-- arch/arm/mach-at91/include/mach/board.h | 7 +++---- drivers/net/at91_ether.c | 2 +- drivers/net/macb.c | 5 ++--- 19 files changed, 23 insertions(+), 25 deletions(-) diff --git a/arch/arm/boards/at91rm9200ek/init.c b/arch/arm/boards/at91rm9200ek/init.c index 439ee9e10..f5d242ff9 100644 --- a/arch/arm/boards/at91rm9200ek/init.c +++ b/arch/arm/boards/at91rm9200ek/init.c @@ -33,7 +33,7 @@ #include static struct at91_ether_platform_data ether_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/at91sam9260ek/init.c b/arch/arm/boards/at91sam9260ek/init.c index d0a8458c9..e9bfee6f0 100644 --- a/arch/arm/boards/at91sam9260ek/init.c +++ b/arch/arm/boards/at91sam9260ek/init.c @@ -128,7 +128,7 @@ static void ek_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/at91sam9263ek/init.c b/arch/arm/boards/at91sam9263ek/init.c index b577f4d2e..45a30fd53 100644 --- a/arch/arm/boards/at91sam9263ek/init.c +++ b/arch/arm/boards/at91sam9263ek/init.c @@ -84,7 +84,7 @@ static void ek_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/at91sam9m10g45ek/init.c b/arch/arm/boards/at91sam9m10g45ek/init.c index f1c43cc98..0c03dea4a 100644 --- a/arch/arm/boards/at91sam9m10g45ek/init.c +++ b/arch/arm/boards/at91sam9m10g45ek/init.c @@ -107,7 +107,7 @@ static void ek_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/at91sam9x5ek/init.c b/arch/arm/boards/at91sam9x5ek/init.c index 562657751..ba5bc470e 100644 --- a/arch/arm/boards/at91sam9x5ek/init.c +++ b/arch/arm/boards/at91sam9x5ek/init.c @@ -106,7 +106,7 @@ static void ek_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/mmccpu/init.c b/arch/arm/boards/mmccpu/init.c index 97248b9a0..67bfcea87 100644 --- a/arch/arm/boards/mmccpu/init.c +++ b/arch/arm/boards/mmccpu/init.c @@ -34,7 +34,7 @@ #include static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_MII | AT91SAM_ETHER_FORCE_LINK, + .phy_flags = PHYLIB_FORCE_LINK, .phy_addr = 4, }; diff --git a/arch/arm/boards/pm9263/init.c b/arch/arm/boards/pm9263/init.c index 14e821fd1..486df9a18 100644 --- a/arch/arm/boards/pm9263/init.c +++ b/arch/arm/boards/pm9263/init.c @@ -90,7 +90,7 @@ static void pm_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/pm9g45/init.c b/arch/arm/boards/pm9g45/init.c index 675ebe851..a79b12808 100644 --- a/arch/arm/boards/pm9g45/init.c +++ b/arch/arm/boards/pm9g45/init.c @@ -114,7 +114,7 @@ static void __init pm9g45_add_device_usbh(void) {} #endif static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = 0, }; diff --git a/arch/arm/boards/qil-a9260/init.c b/arch/arm/boards/qil-a9260/init.c index 8a12b171d..3bec4e2d2 100644 --- a/arch/arm/boards/qil-a9260/init.c +++ b/arch/arm/boards/qil-a9260/init.c @@ -79,7 +79,7 @@ static void qil_a9260_add_device_mci(void) {} #ifdef CONFIG_CALAO_MB_QIL_A9260 static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = -1, }; diff --git a/arch/arm/boards/tny-a926x/init.c b/arch/arm/boards/tny-a926x/init.c index 270becf3d..98f1d2b52 100644 --- a/arch/arm/boards/tny-a926x/init.c +++ b/arch/arm/boards/tny-a926x/init.c @@ -114,7 +114,7 @@ static void tny_a9260_add_device_nand(void) #ifdef CONFIG_DRIVER_NET_MACB static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = -1, }; diff --git a/arch/arm/boards/usb-a926x/init.c b/arch/arm/boards/usb-a926x/init.c index e0df0264a..5190aca8a 100644 --- a/arch/arm/boards/usb-a926x/init.c +++ b/arch/arm/boards/usb-a926x/init.c @@ -114,7 +114,7 @@ static void usb_a9260_add_device_nand(void) } static struct at91_ether_platform_data macb_pdata = { - .flags = AT91SAM_ETHER_RMII, + .is_rmii = 1, .phy_addr = -1, }; diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index a67643639..47516646d 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c @@ -91,7 +91,7 @@ void __init at91_add_device_eth(int id, struct at91_ether_platform_data *data) at91_set_A_periph(AT91_PIN_PA8, 0); /* ETXEN */ at91_set_A_periph(AT91_PIN_PA7, 0); /* ETXCK_EREFCK */ - if (!(data->flags & AT91SAM_ETHER_RMII)) { + if (!data->is_rmii) { at91_set_B_periph(AT91_PIN_PB19, 0); /* ERXCK */ at91_set_B_periph(AT91_PIN_PB18, 0); /* ECOL */ at91_set_B_periph(AT91_PIN_PB17, 0); /* ERXDV */ diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 7450ec11a..04a9c5f1e 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -89,7 +89,7 @@ void at91_add_device_eth(int id, struct at91_ether_platform_data *data) at91_set_A_periph(AT91_PIN_PA21, 0); /* EMDIO */ at91_set_A_periph(AT91_PIN_PA20, 0); /* EMDC */ - if (!(data->flags & AT91SAM_ETHER_RMII)) { + if (!data->is_rmii) { at91_set_B_periph(AT91_PIN_PA28, 0); /* ECRS */ at91_set_B_periph(AT91_PIN_PA29, 0); /* ECOL */ at91_set_B_periph(AT91_PIN_PA25, 0); /* ERX2 */ diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index deffc27b1..fcd157d2c 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -96,7 +96,7 @@ void at91_add_device_eth(int id, struct at91_ether_platform_data *data) at91_set_A_periph(AT91_PIN_PE30, 0); /* EMDIO */ at91_set_A_periph(AT91_PIN_PE29, 0); /* EMDC */ - if (!(data->flags & AT91SAM_ETHER_RMII)) { + if (!data->is_rmii) { at91_set_A_periph(AT91_PIN_PE22, 0); /* ECRS */ at91_set_B_periph(AT91_PIN_PC26, 0); /* ECOL */ at91_set_B_periph(AT91_PIN_PC22, 0); /* ERX2 */ diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 35cd2e722..252940423 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -72,7 +72,7 @@ void at91_add_device_eth(int id, struct at91_ether_platform_data *data) at91_set_A_periph(AT91_PIN_PA19, 0); /* EMDIO */ at91_set_A_periph(AT91_PIN_PA18, 0); /* EMDC */ - if (!(data->flags & AT91SAM_ETHER_RMII)) { + if (!data->is_rmii) { at91_set_B_periph(AT91_PIN_PA29, 0); /* ECRS */ at91_set_B_periph(AT91_PIN_PA30, 0); /* ECOL */ at91_set_B_periph(AT91_PIN_PA8, 0); /* ERX2 */ diff --git a/arch/arm/mach-at91/at91sam9x5_devices.c b/arch/arm/mach-at91/at91sam9x5_devices.c index b804b4134..c2b64e980 100644 --- a/arch/arm/mach-at91/at91sam9x5_devices.c +++ b/arch/arm/mach-at91/at91sam9x5_devices.c @@ -84,7 +84,7 @@ void at91_add_device_eth(int id, struct at91_ether_platform_data *data) at91_set_A_periph(AT91_PIN_PB5, 0); /* EMDIO */ at91_set_A_periph(AT91_PIN_PB6, 0); /* EMDC */ - if (!(data->flags & AT91SAM_ETHER_RMII)) { + if (!data->is_rmii) { at91_set_A_periph(AT91_PIN_PB16, 0); /* ECRS */ at91_set_A_periph(AT91_PIN_PB17, 0); /* ECOL */ at91_set_A_periph(AT91_PIN_PB13, 0); /* ERX2 */ @@ -97,7 +97,7 @@ void at91_add_device_eth(int id, struct at91_ether_platform_data *data) break; case 1: start = AT91SAM9X5_BASE_EMAC1; - if (!(data->flags & AT91SAM_ETHER_RMII)) + if (!data->is_rmii) pr_warn("AT91: Only RMII available on interface macb%d.\n", id); /* Pins used for RMII */ diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index dcea9f395..433915f9c 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -63,14 +63,13 @@ struct atmel_nand_data { void at91_add_device_nand(struct atmel_nand_data *data); /* Ethernet (EMAC & MACB) */ -#define AT91SAM_ETHER_MII (0 << 0) -#define AT91SAM_ETHER_RMII (1 << 0) -#define AT91SAM_ETHER_FORCE_LINK (1 << 1) -#define AT91SAM_ETX2_ETX3_ALTERNATIVE (1 << 2) +#define AT91SAM_ETX2_ETX3_ALTERNATIVE (1 << 0) struct at91_ether_platform_data { + unsigned int phy_flags; unsigned int flags; int phy_addr; + int is_rmii; int (*get_ethaddr)(struct eth_device*, unsigned char *adr); }; diff --git a/drivers/net/at91_ether.c b/drivers/net/at91_ether.c index a6d6183f1..2e3a271d1 100644 --- a/drivers/net/at91_ether.c +++ b/drivers/net/at91_ether.c @@ -342,7 +342,7 @@ static int at91_ether_probe(struct device_d *dev) mac_cfg |= AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG; - if (pdata->flags & AT91SAM_ETHER_RMII) { + if (pdata->is_rmii) { ether_dev->interface = PHY_INTERFACE_MODE_RGMII; mac_cfg |= AT91_EMAC_RMII; } else { diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ce3ef660f..35e5e58c4 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -439,13 +439,12 @@ static int macb_probe(struct device_d *dev) macb->miibus.priv = macb; macb->miibus.parent = dev; - if (pdata->flags & AT91SAM_ETHER_RMII) + if (pdata->is_rmii) macb->interface = PHY_INTERFACE_MODE_RMII; else macb->interface = PHY_INTERFACE_MODE_MII; - macb->phy_flags = pdata->flags & AT91SAM_ETHER_FORCE_LINK ? - PHYLIB_FORCE_LINK : 0; + macb->phy_flags = pdata->phy_flags; macb->rx_buffer = dma_alloc_coherent(CFG_MACB_RX_BUFFER_SIZE); macb->rx_ring = dma_alloc_coherent(CFG_MACB_RX_RING_SIZE * sizeof(struct macb_dma_desc)); From f777b3b13fab090ad0f340c626c05176b4bf226d Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Nov 2012 13:49:39 +0100 Subject: [PATCH 4/9] driver: introduce device_probe to manully probe a device This will expect a driver to be specified This is needed by the phylib the probe the generic phy if not driver found Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/base/driver.c | 23 +++++++++++++++++------ include/driver.h | 5 +++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index dc2df9152..1f4031ac9 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -75,6 +75,21 @@ int get_free_deviceid(const char *name_template) }; } +int device_probe(struct device_d *dev) +{ + int ret; + + ret = dev->bus->probe(dev); + if (ret) { + dev_err(dev, "probe failed: %s\n", strerror(-ret)); + return ret; + } + + list_add(&dev->active, &active); + + return 0; +} + static int match(struct driver_d *drv, struct device_d *dev) { int ret; @@ -86,13 +101,9 @@ static int match(struct driver_d *drv, struct device_d *dev) if (dev->bus->match(dev, drv)) goto err_out; - ret = dev->bus->probe(dev); - if (ret) { - dev_err(dev, "probe failed: %s\n", strerror(-ret)); + ret = device_probe(dev); + if (ret) goto err_out; - } - - list_add(&dev->active, &active); return 0; err_out: diff --git a/include/driver.h b/include/driver.h index f8d815c61..a08598174 100644 --- a/include/driver.h +++ b/include/driver.h @@ -148,6 +148,11 @@ struct driver_d { int register_driver(struct driver_d *); int register_device(struct device_d *); +/* manualy probe a device + * the driver need to be specified + */ +int device_probe(struct device_d *dev); + /* Unregister a device. This function can fail, e.g. when the device * has children. */ From 9ea956db779f485c243c91f127d569ba93f18dd3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Nov 2012 13:49:40 +0100 Subject: [PATCH 5/9] phylib: fix generic phy driver probe the generic phy driver is used if no driver are found Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 8 ++------ drivers/net/phy/Makefile | 1 - drivers/net/phy/generic.c | 36 ------------------------------------ drivers/net/phy/phy.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 34 insertions(+), 45 deletions(-) delete mode 100644 drivers/net/phy/generic.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 616b53988..3f7c150a8 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -8,15 +8,11 @@ if PHYLIB comment "MII PHY device drivers" -config GENERIC_PHY - bool "Drivers for the Generic PHYs" - default y - -endif - config SMSC_PHY bool "Drivers for SMSC PHYs" ---help--- Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +endif + endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 0caeb7058..dfc709a82 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,3 +1,2 @@ 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/generic.c b/drivers/net/phy/generic.c deleted file mode 100644 index 3f5f12706..000000000 --- a/drivers/net/phy/generic.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2009 Jean-Christophe PLAGNIOL-VILLARD - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * - */ - -#include -#include -#include - -static struct phy_driver generic_phy = { - .drv.name = "Generic PHY", - .phy_id = PHY_ANY_UID, - .phy_id_mask = PHY_ANY_UID, - .features = 0, -}; - -static int generic_phy_register(void) -{ - return phy_driver_register(&generic_phy); -} -device_initcall(generic_phy_register); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4478d9f5d..2fd0440a0 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #define PHY_AN_TIMEOUT 10 +static struct phy_driver genphy_driver; static int genphy_config_init(struct phy_device *phydev); int phy_update_status(struct phy_device *dev) @@ -142,6 +144,21 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr) return dev; } +static int phy_register_device(struct phy_device* dev) +{ + int ret; + + ret = register_device(&dev->dev); + if (ret) + return ret; + + if (dev->dev.driver) + return 0; + + dev->dev.driver = &genphy_driver.drv; + return device_probe(&dev->dev); +} + /* Automatically gets and returns the PHY device */ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), @@ -164,7 +181,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, dev->interface = interface; dev->dev_flags = flags; - ret = register_device(&dev->dev); + ret = phy_register_device(dev); if (ret) goto fail; } else { @@ -181,7 +198,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, dev->interface = interface; dev->dev_flags = flags; - ret = register_device(&dev->dev); + ret = phy_register_device(dev); if (ret) goto fail; @@ -597,3 +614,16 @@ int phy_drivers_register(struct phy_driver *new_driver, int n) } return ret; } + +static struct phy_driver genphy_driver = { + .drv.name = "Generic PHY", + .phy_id = PHY_ANY_UID, + .phy_id_mask = PHY_ANY_UID, + .features = 0, +}; + +static int generic_phy_register(void) +{ + return phy_driver_register(&genphy_driver); +} +device_initcall(generic_phy_register); From 30298c0f3ce43c364090f14618ccf012260841ca Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Nov 2012 13:49:41 +0100 Subject: [PATCH 6/9] mdio_bus: fix match on barebox we have the weird way the return 0 true on bus match Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 544d743d9..3e7934508 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -106,7 +106,7 @@ static int mdio_bus_match(struct device_d *dev, struct driver_d *drv) struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); - return ((phydrv->phy_id & phydrv->phy_id_mask) == + return ((phydrv->phy_id & phydrv->phy_id_mask) != (phydev->phy_id & phydrv->phy_id_mask)); } From 4a1e4d4b1da62a02f6ca32f44b709326bd764efc Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Nov 2012 13:49:44 +0100 Subject: [PATCH 7/9] phylib: add fixup support if board need specific phy fixup they can register it and then the code will executed only if needed Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 19 ++++---- drivers/net/phy/phy.c | 96 ++++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 18 +++++++ 3 files changed, 125 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 3e7934508..1d20bb050 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -153,6 +153,7 @@ static int mdio_bus_probe(struct device_d *_dev) struct phy_device *dev = to_phy_device(_dev); struct phy_driver *drv = to_phy_driver(_dev->driver); + int ret; char str[16]; dev->attached_dev->phydev = dev; @@ -160,14 +161,9 @@ static int mdio_bus_probe(struct device_d *_dev) dev_add_child(dev->dev.parent, _dev); if (drv->probe) { - int ret; - ret = drv->probe(dev); - if (ret) { - dev->attached_dev->phydev = NULL; - dev->attached_dev = NULL; - return ret; - } + if (ret) + goto err; } if (dev->dev_flags) { @@ -188,7 +184,9 @@ static int mdio_bus_probe(struct device_d *_dev) dev->supported = drv->features; dev->advertising = drv->features; - drv->config_init(dev); + ret = phy_init_hw(dev); + if (ret) + goto err; /* Sanitize settings based on PHY capabilities */ if ((dev->supported & SUPPORTED_Autoneg) == 0) @@ -208,6 +206,11 @@ static int mdio_bus_probe(struct device_d *_dev) devfs_create(&dev->cdev); return 0; + +err: + dev->attached_dev->phydev = NULL; + dev->attached_dev = NULL; + return ret; } static void mdio_bus_remove(struct device_d *_dev) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2fd0440a0..59df74252 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -55,6 +55,87 @@ int phy_update_status(struct phy_device *dev) return 0; } +static LIST_HEAD(phy_fixup_list); + +/* + * Creates a new phy_fixup and adds it to the list + * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) + * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) + * It can also be PHY_ANY_UID + * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before + * comparison + * @run: The actual code to be run when a matching PHY is found + */ +int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)) +{ + struct phy_fixup *fixup; + + fixup = kzalloc(sizeof(struct phy_fixup), GFP_KERNEL); + if (!fixup) + return -ENOMEM; + + strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id)); + fixup->phy_uid = phy_uid; + fixup->phy_uid_mask = phy_uid_mask; + fixup->run = run; + + list_add_tail(&fixup->list, &phy_fixup_list); + + return 0; +} + +/* Registers a fixup to be run on any PHY with the UID in phy_uid */ +int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)) +{ + return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run); +} + +/* Registers a fixup to be run on the PHY with id string bus_id */ +int phy_register_fixup_for_id(const char *bus_id, + int (*run)(struct phy_device *)) +{ + return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run); +} + +/* + * Returns 1 if fixup matches phydev in bus_id and phy_uid. + * Fixups can be set to match any in one or more fields. + */ +static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) +{ + if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0) + if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) + return 0; + + if ((fixup->phy_uid & fixup->phy_uid_mask) != + (phydev->phy_id & fixup->phy_uid_mask)) + if (fixup->phy_uid != PHY_ANY_UID) + return 0; + + return 1; +} +/* Runs any matching fixups for this phydev */ +int phy_scan_fixups(struct phy_device *phydev) +{ + struct phy_fixup *fixup; + + list_for_each_entry(fixup, &phy_fixup_list, list) { + if (phy_needs_fixup(phydev, fixup)) { + int err; + + err = fixup->run(phydev); + + if (err < 0) { + return err; + } + } + } + + return 0; +} + struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *dev; @@ -615,6 +696,21 @@ int phy_drivers_register(struct phy_driver *new_driver, int n) return ret; } +int phy_init_hw(struct phy_device *phydev) +{ + struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + int ret; + + if (!phydrv || !phydrv->config_init) + return 0; + + ret = phy_scan_fixups(phydev); + if (ret < 0) + return ret; + + return phydrv->config_init(phydev); +} + static struct phy_driver genphy_driver = { .drv.name = "Generic PHY", .phy_id = PHY_ANY_UID, diff --git a/include/linux/phy.h b/include/linux/phy.h index 791d657c9..b39eca5fb 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -226,10 +226,20 @@ struct phy_driver { #define PHY_ANY_ID "MATCH ANY PHY" #define PHY_ANY_UID 0xffffffff +/* A Structure for boards to register fixups with the PHY Lib */ +struct phy_fixup { + struct list_head list; + char bus_id[20]; + u32 phy_uid; + u32 phy_uid_mask; + int (*run)(struct phy_device *phydev); +}; + 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); +int phy_init_hw(struct phy_device *phydev); /** * phy_read - Convenience function for reading a given PHY register @@ -267,5 +277,13 @@ int genphy_config_advert(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id); +int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); +int phy_register_fixup_for_id(const char *bus_id, + int (*run)(struct phy_device *)); +int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); +int phy_scan_fixups(struct phy_device *phydev); + extern struct bus_type mdio_bus_type; #endif /* __PHYDEV_H__ */ From 34bcbaa2bab148e564272f0955cfd1e0f10862fb Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Nov 2012 13:49:45 +0100 Subject: [PATCH 8/9] phylib: add micrel support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 5 ++ drivers/net/phy/Makefile | 1 + drivers/net/phy/micrel.c | 174 +++++++++++++++++++++++++++++++++++++ include/linux/micrel_phy.h | 30 +++++++ 4 files changed, 210 insertions(+) create mode 100644 drivers/net/phy/micrel.c create mode 100644 include/linux/micrel_phy.h diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3f7c150a8..c75d73dd0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -8,6 +8,11 @@ if PHYLIB comment "MII PHY device drivers" +config MICREL_PHY + bool "Driver for Micrel PHYs" + ---help--- + Supports the KSZ9021, VSC8201, KS8001 PHYs. + config SMSC_PHY bool "Drivers for SMSC PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index dfc709a82..5f8191d8a 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,2 +1,3 @@ obj-y += phy.o mdio_bus.o +obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c new file mode 100644 index 000000000..0c6d43e61 --- /dev/null +++ b/drivers/net/phy/micrel.c @@ -0,0 +1,174 @@ +/* + * drivers/net/phy/micrel.c + * + * Driver for Micrel PHYs + * + * Author: David J. Choi + * + * Copyright (c) 2010 Micrel, Inc. + * + * 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 : ksz9021 1000/100/10 phy from Micrel + * ks8001, ks8737, ks8721, ks8041, ks8051 100/10 phy + */ + +#include +#include +#include +#include +#include +#include + +/* Operation Mode Strap Override */ +#define MII_KSZPHY_OMSO 0x16 +#define KSZPHY_OMSO_B_CAST_OFF (1 << 9) +#define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1) +#define KSZPHY_OMSO_MII_OVERRIDE (1 << 0) + +/* general PHY control reg in vendor specific block. */ +#define MII_KSZPHY_CTRL 0x1F +/* bitmap of PHY register to set interrupt mode */ +#define KSZPHY_CTRL_INT_ACTIVE_HIGH (1 << 9) +#define KSZ9021_CTRL_INT_ACTIVE_HIGH (1 << 14) +#define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14) +#define KSZ8051_RMII_50MHZ_CLK (1 << 7) + +static int kszphy_config_init(struct phy_device *phydev) +{ + return 0; +} + +static int ksz8021_config_init(struct phy_device *phydev) +{ + const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE; + phy_write(phydev, MII_KSZPHY_OMSO, val); + return 0; +} + +static int ks8051_config_init(struct phy_device *phydev) +{ + int regval; + + if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { + regval = phy_read(phydev, MII_KSZPHY_CTRL); + regval |= KSZ8051_RMII_50MHZ_CLK; + phy_write(phydev, MII_KSZPHY_CTRL, regval); + } + + return 0; +} + +#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 +#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6) +#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4) +int ksz8873mll_read_status(struct phy_device *phydev) +{ + int regval; + + /* dummy read */ + regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + + regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + + if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) + phydev->duplex = DUPLEX_HALF; + else + phydev->duplex = DUPLEX_FULL; + + if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) + phydev->speed = SPEED_10; + else + phydev->speed = SPEED_100; + + phydev->link = 1; + phydev->pause = phydev->asym_pause = 0; + + return 0; +} + +static int ksz8873mll_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int ksz8873mll_config_init(struct phy_device *phydev) +{ + phydev->autoneg = AUTONEG_DISABLE; + phydev->link = 1; + + return 0; +} + +static struct phy_driver ksphy_driver[] = { +{ + .phy_id = PHY_ID_KS8737, + .phy_id_mask = 0x00fffff0, + .drv.name = "Micrel KS8737", + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ8021, + .phy_id_mask = 0x00ffffff, + .drv.name = "Micrel KSZ8021", + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause), + .config_init = ksz8021_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ8041, + .phy_id_mask = 0x00fffff0, + .drv.name = "Micrel KSZ8041", + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ8051, + .phy_id_mask = 0x00fffff0, + .drv.name = "Micrel KSZ8051", + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .config_init = ks8051_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ8001, + .drv.name = "Micrel KSZ8001 or KS8721", + .phy_id_mask = 0x00ffffff, + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ9021, + .phy_id_mask = 0x000ffffe, + .drv.name = "Micrel KSZ9021 Gigabit PHY", + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ8873MLL, + .phy_id_mask = 0x00fffff0, + .drv.name = "Micrel KSZ8873MLL Swithch", + .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), + .config_init = ksz8873mll_config_init, + .config_aneg = ksz8873mll_config_aneg, + .read_status = ksz8873mll_read_status, +} }; + +static int ksphy_init(void) +{ + return phy_drivers_register(ksphy_driver, + ARRAY_SIZE(ksphy_driver)); +} +fs_initcall(ksphy_init); diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h new file mode 100644 index 000000000..adfe8c058 --- /dev/null +++ b/include/linux/micrel_phy.h @@ -0,0 +1,30 @@ +/* + * include/linux/micrel_phy.h + * + * Micrel PHY IDs + * + * 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. + * + */ + +#ifndef _MICREL_PHY_H +#define _MICREL_PHY_H + +#define MICREL_PHY_ID_MASK 0x00fffff0 + +#define PHY_ID_KSZ8873MLL 0x000e7237 +#define PHY_ID_KSZ9021 0x00221610 +#define PHY_ID_KS8737 0x00221720 +#define PHY_ID_KSZ8021 0x00221555 +#define PHY_ID_KSZ8041 0x00221510 +#define PHY_ID_KSZ8051 0x00221550 +/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */ +#define PHY_ID_KSZ8001 0x0022161A + +/* struct phy_device dev_flags definitions */ +#define MICREL_PHY_50MHZ_CLK 0x00000001 + +#endif /* _MICREL_PHY_H */ From bba73109ce1f54c7ffca044b00aacb320be888c2 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Wed, 5 Dec 2012 18:15:39 +0100 Subject: [PATCH 9/9] net: phy: micrel: fix typo in driver name Signed-off-by: Jan Luebbe Signed-off-by: Sascha Hauer --- drivers/net/phy/micrel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 0c6d43e61..ed5abf80b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -159,7 +159,7 @@ static struct phy_driver ksphy_driver[] = { }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, - .drv.name = "Micrel KSZ8873MLL Swithch", + .drv.name = "Micrel KSZ8873MLL Switch", .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), .config_init = ksz8873mll_config_init, .config_aneg = ksz8873mll_config_aneg,