diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2016-02-08 08:26:45 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-02-08 08:26:45 +0100 |
commit | a22f59c178355ea3efe376739591a43c8ac877d1 (patch) | |
tree | f7c25bae127dab722dc5323740b6a2da069a4aae /drivers | |
parent | 1379a5ef1e5229d9ea71293c7bb6cb89e6d706a4 (diff) | |
parent | b5778f9773df9e749158e43976a4d88e84ef79f8 (diff) | |
download | barebox-a22f59c178355ea3efe376739591a43c8ac877d1.tar.gz barebox-a22f59c178355ea3efe376739591a43c8ac877d1.tar.xz |
Merge branch 'for-next/net'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/phy/Kconfig | 15 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bitbang.c | 228 | ||||
-rw-r--r-- | drivers/net/phy/mdio-gpio.c | 231 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 84 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 15 |
7 files changed, 570 insertions, 30 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d0a02c1e40..d30f65b8e6 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -46,6 +46,21 @@ config MDIO_MVEBU ---help--- Driver for the MDIO bus found on Marvell EBU SoCs. +config MDIO_BITBANG + bool "Support for bitbanged MDIO buses" + ---help--- + This module implements the MDIO bus protocol in software, + for use by low level drivers that export the ability to + drive the relevant pins. + + If in doubt, say N. + +config MDIO_GPIO + bool "Support for GPIO lib-based bitbanged MDIO buses" + depends on MDIO_BITBANG && GPIOLIB + ---help--- + Supports GPIO lib-based MDIO busses. + endif endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 94b9be83ea..10732f8070 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o +obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o +obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c new file mode 100644 index 0000000000..4f610e375f --- /dev/null +++ b/drivers/net/phy/mdio-bitbang.c @@ -0,0 +1,228 @@ +/* + * Bitbanged MDIO support. + * + * Author: Scott Wood <scottwood@freescale.com> + * Copyright (c) 2007 Freescale Semiconductor + * + * Based on CPM2 MDIO code which is: + * + * Copyright (c) 2003 Intracom S.A. + * by Pantelis Antoniou <panto@intracom.gr> + * + * 2005 (c) MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <common.h> +#include <driver.h> +#include <linux/phy.h> +#include <linux/mdio-bitbang.h> + +#define MDIO_READ 2 +#define MDIO_WRITE 1 + +#define MDIO_C45 (1<<15) +#define MDIO_C45_ADDR (MDIO_C45 | 0) +#define MDIO_C45_READ (MDIO_C45 | 3) +#define MDIO_C45_WRITE (MDIO_C45 | 1) + +#define MDIO_SETUP_TIME 10 +#define MDIO_HOLD_TIME 10 + +/* Minimum MDC period is 400 ns, plus some margin for error. MDIO_DELAY + * is done twice per period. + */ +#define MDIO_DELAY 250 + +/* The PHY may take up to 300 ns to produce data, plus some margin + * for error. + */ +#define MDIO_READ_DELAY 350 + +/* MDIO must already be configured as output. */ +static void mdiobb_send_bit(struct mdiobb_ctrl *ctrl, int val) +{ + const struct mdiobb_ops *ops = ctrl->ops; + + ops->set_mdio_data(ctrl, val); + ndelay(MDIO_DELAY); + ops->set_mdc(ctrl, 1); + ndelay(MDIO_DELAY); + ops->set_mdc(ctrl, 0); +} + +/* MDIO must already be configured as input. */ +static int mdiobb_get_bit(struct mdiobb_ctrl *ctrl) +{ + const struct mdiobb_ops *ops = ctrl->ops; + + ndelay(MDIO_DELAY); + ops->set_mdc(ctrl, 1); + ndelay(MDIO_READ_DELAY); + ops->set_mdc(ctrl, 0); + + return ops->get_mdio_data(ctrl); +} + +/* MDIO must already be configured as output. */ +static void mdiobb_send_num(struct mdiobb_ctrl *ctrl, u16 val, int bits) +{ + int i; + + for (i = bits - 1; i >= 0; i--) + mdiobb_send_bit(ctrl, (val >> i) & 1); +} + +/* MDIO must already be configured as input. */ +static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits) +{ + int i; + u16 ret = 0; + + for (i = bits - 1; i >= 0; i--) { + ret <<= 1; + ret |= mdiobb_get_bit(ctrl); + } + + return ret; +} + +/* Utility to send the preamble, address, and + * register (common to read and write). + */ +static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg) +{ + const struct mdiobb_ops *ops = ctrl->ops; + int i; + + ops->set_mdio_dir(ctrl, 1); + + /* + * Send a 32 bit preamble ('1's) with an extra '1' bit for good + * measure. The IEEE spec says this is a PHY optional + * requirement. The AMD 79C874 requires one after power up and + * one after a MII communications error. This means that we are + * doing more preambles than we need, but it is safer and will be + * much more robust. + */ + + for (i = 0; i < 32; i++) + mdiobb_send_bit(ctrl, 1); + + /* send the start bit (01) and the read opcode (10) or write (10). + Clause 45 operation uses 00 for the start and 11, 10 for + read/write */ + mdiobb_send_bit(ctrl, 0); + if (op & MDIO_C45) + mdiobb_send_bit(ctrl, 0); + else + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, (op >> 1) & 1); + mdiobb_send_bit(ctrl, (op >> 0) & 1); + + mdiobb_send_num(ctrl, phy, 5); + mdiobb_send_num(ctrl, reg, 5); +} + +/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the + lower 16 bits of the 21 bit address. This transfer is done identically to a + MDIO_WRITE except for a different code. To enable clause 45 mode or + MII_ADDR_C45 into the address. Theoretically clause 45 and normal devices + can exist on the same bus. Normal devices should ignore the MDIO_ADDR + phase. */ +static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) +{ + unsigned int dev_addr = (addr >> 16) & 0x1F; + unsigned int reg = addr & 0xFFFF; + mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr); + + /* send the turnaround (10) */ + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, 0); + + mdiobb_send_num(ctrl, reg, 16); + + ctrl->ops->set_mdio_dir(ctrl, 0); + mdiobb_get_bit(ctrl); + + return dev_addr; +} + +static int mdiobb_read(struct mii_bus *bus, int phy, int reg) +{ + struct mdiobb_ctrl *ctrl = bus->priv; + int ret, i; + + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + + ctrl->ops->set_mdio_dir(ctrl, 0); + + /* check the turnaround bit: the PHY should be driving it to zero, if this + * PHY is listed in phy_ignore_ta_mask as having broken TA, skip that + */ + if (mdiobb_get_bit(ctrl) != 0) { + /* PHY didn't drive TA low -- flush any bits it + * may be trying to send. + */ + for (i = 0; i < 32; i++) + mdiobb_get_bit(ctrl); + + return 0xffff; + } + + ret = mdiobb_get_num(ctrl, 16); + mdiobb_get_bit(ctrl); + return ret; +} + +static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + struct mdiobb_ctrl *ctrl = bus->priv; + + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + + /* send the turnaround (10) */ + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, 0); + + mdiobb_send_num(ctrl, val, 16); + + ctrl->ops->set_mdio_dir(ctrl, 0); + mdiobb_get_bit(ctrl); + return 0; +} + +static int mdiobb_reset(struct mii_bus *bus) +{ + struct mdiobb_ctrl *ctrl = bus->priv; + if (ctrl->reset) + ctrl->reset(bus); + return 0; +} + +struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) +{ + struct mii_bus *bus; + + bus = xzalloc(sizeof(*bus)); + + bus->read = mdiobb_read; + bus->write = mdiobb_write; + bus->reset = mdiobb_reset; + bus->priv = ctrl; + + return bus; +} +EXPORT_SYMBOL(alloc_mdio_bitbang); diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c new file mode 100644 index 0000000000..a839f2dee8 --- /dev/null +++ b/drivers/net/phy/mdio-gpio.c @@ -0,0 +1,231 @@ +/* + * GPIO based MDIO bitbang driver. + * Supports OpenFirmware. + * + * (C) Copyright 2015 + * CogentEmbedded, Andrey Gusakov <andrey.gusakov@cogentembedded.com> + * + * based on mvmdio driver from Linux + * Copyright (c) 2008 CSE Semaphore Belgium. + * by Laurent Pinchart <laurentp@cse-semaphore.com> + * + * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> + * + * Based on earlier work by + * + * Copyright (c) 2003 Intracom S.A. + * by Pantelis Antoniou <panto@intracom.gr> + * + * 2005 (c) MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#define DEBUG + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <of_gpio.h> +#include <linux/phy.h> +#include <gpio.h> +#include <malloc.h> +#include <xfuncs.h> + +#include <linux/mdio-bitbang.h> + +struct mdio_gpio_info { + struct mdiobb_ctrl ctrl; + int mdc, mdio, mdo; + int mdc_active_low, mdio_active_low, mdo_active_low; +}; + +struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) +{ + int ret; + enum of_gpio_flags flags; + struct mdio_gpio_info *info; + + info = xzalloc(sizeof(*info)); + + ret = of_get_gpio_flags(dev->device_node, 0, &flags); + if (ret < 0) { + dev_dbg(dev, "failed to get MDC inforamtion from DT\n"); + goto free_info; + } + + info->mdc = ret; + info->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW; + + ret = of_get_gpio_flags(dev->device_node, 1, &flags); + if (ret < 0) { + dev_dbg(dev, "failed to get MDIO inforamtion from DT\n"); + goto free_info; + } + + info->mdio = ret; + info->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW; + + ret = of_get_gpio_flags(dev->device_node, 2, &flags); + if (ret > 0) { + dev_dbg(dev, "found MDO information in DT\n"); + info->mdo = ret; + info->mdo_active_low = flags & OF_GPIO_ACTIVE_LOW; + } + + return info; + +free_info: + free(info); + return ERR_PTR(ret); +} + +static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + if (bitbang->mdo) { + /* Separate output pin. Always set its value to high + * when changing direction. If direction is input, + * assume the pin serves as pull-up. If direction is + * output, the default value is high. + */ + gpio_set_value(bitbang->mdo, + 1 ^ bitbang->mdo_active_low); + return; + } + + if (dir) + gpio_direction_output(bitbang->mdio, + 1 ^ bitbang->mdio_active_low); + else + gpio_direction_input(bitbang->mdio); +} + +static int mdio_get(struct mdiobb_ctrl *ctrl) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + return gpio_get_value(bitbang->mdio) ^ + bitbang->mdio_active_low; +} + +static void mdio_set(struct mdiobb_ctrl *ctrl, int what) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + if (bitbang->mdo) + gpio_set_value(bitbang->mdo, + what ^ bitbang->mdo_active_low); + else + gpio_set_value(bitbang->mdio, + what ^ bitbang->mdio_active_low); +} + +static void mdc_set(struct mdiobb_ctrl *ctrl, int what) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + gpio_set_value(bitbang->mdc, what ^ bitbang->mdc_active_low); +} + +static struct mdiobb_ops mdio_gpio_ops = { + .set_mdc = mdc_set, + .set_mdio_dir = mdio_dir, + .set_mdio_data = mdio_set, + .get_mdio_data = mdio_get, +}; + +static int mdio_gpio_probe(struct device_d *dev) +{ + int ret; + struct device_node *np = dev->device_node; + struct mdio_gpio_info *info; + struct mii_bus *bus; + + info = mdio_gpio_of_get_info(dev); + if (IS_ERR(info)) + return PTR_ERR(info); + + info->ctrl.ops = &mdio_gpio_ops; + + ret = gpio_request(info->mdc, "mdc"); + if (ret < 0) { + dev_dbg(dev, "failed to request MDC\n"); + goto free_info; + } + + ret = gpio_request(info->mdio, "mdio"); + if (ret < 0) { + dev_dbg(dev, "failed to request MDIO\n"); + goto free_mdc; + } + + if (info->mdo) { + ret = gpio_request(info->mdo, "mdo"); + if (ret < 0) { + dev_dbg(dev, "failed to request MDO\n"); + goto free_mdio; + } + + ret = gpio_direction_output(info->mdo, 1); + if (ret < 0) { + dev_dbg(dev, "failed to set MDO as output\n"); + goto free_mdo; + } + + ret = gpio_direction_input(info->mdio); + if (ret < 0) { + dev_dbg(dev, "failed to set MDIO as input\n"); + goto free_mdo; + } + } + + ret = gpio_direction_output(info->mdc, 0); + if (ret < 0) { + dev_dbg(dev, "failed to set MDC as output\n"); + goto free_mdo; + } + + bus = alloc_mdio_bitbang(&info->ctrl); + bus->parent = dev; + bus->dev.device_node = np; + + dev->priv = bus; + + ret = mdiobus_register(bus); + if (!ret) + return 0; + + free(bus); +free_mdo: + gpio_free(info->mdo); +free_mdc: + gpio_free(info->mdc); +free_mdio: + gpio_free(info->mdio); +free_info: + free(info); + return ret; +} + +static const struct of_device_id gpio_mdio_dt_ids[] = { + { .compatible = "virtual,mdio-gpio", }, + { /* sentinel */ } +}; + +static struct driver_d mdio_gpio_driver = { + .name = "mdio-gpio", + .probe = mdio_gpio_probe, + .of_compatible = DRV_OF_COMPAT(gpio_mdio_dt_ids), +}; +device_platform_driver(mdio_gpio_driver); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 0959c45e62..41bf018141 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -43,7 +43,7 @@ int mdiobus_detect(struct device_d *dev) ret = phy_register_device(phydev); if (ret) dev_err(dev, "failed to register phy: %s\n", strerror(-ret)); - dev_info(dev, "registered phy as /dev/%s\n", phydev->cdev.name); + dev_dbg(dev, "registered phy as /dev/%s\n", phydev->cdev.name); } return 0; @@ -187,6 +187,25 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) } EXPORT_SYMBOL(mdiobus_scan); + +/** + * + * mdio_get_bus - get a MDIO bus from its busnum + * + * @param busnum the desired bus number + * + */ +struct mii_bus *mdiobus_get_bus(int busnum) +{ + struct mii_bus *mii; + + for_each_mii_bus(mii) + if (mii->dev.id == busnum) + return mii; + + return NULL; +} + /** * mdio_bus_match - determine if given PHY driver supports the given PHY device * @dev: target PHY device @@ -314,7 +333,9 @@ static int mdio_bus_probe(struct device_d *_dev) dev_add_param_int_ro(&dev->dev, "phy_addr", dev->addr, "%d"); dev_add_param_int_ro(&dev->dev, "phy_id", dev->phy_id, "0x%08x"); - dev->cdev.name = asprintf("phy%d", _dev->id); + dev->cdev.name = xasprintf("mdio%d-phy%02x", + dev->bus->dev.id, + dev->addr); dev->cdev.size = 64; dev->cdev.ops = &phydev_ops; dev->cdev.priv = dev; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 095563ad3b..e8a566deba 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -25,17 +25,17 @@ /* 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) +#define KSZPHY_OMSO_B_CAST_OFF BIT(9) +#define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) +#define KSZPHY_OMSO_MII_OVERRIDE BIT(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) +#define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) +#define KSZ9021_CTRL_INT_ACTIVE_HIGH BIT(14) +#define KS8737_CTRL_INT_ACTIVE_HIGH BIT(14) +#define KSZ8051_RMII_50MHZ_CLK BIT(7) /* Write/read to/from extended registers */ #define MII_KSZPHY_EXTREG 0x0b @@ -91,8 +91,8 @@ static int ks8051_config_init(struct phy_device *phydev) } static int ksz9021_load_values_from_of(struct phy_device *phydev, - struct device_node *of_node, u16 reg, - const char *field[]) + const struct device_node *of_node, + u16 reg, const char *field[]) { int val, regval, i; @@ -113,8 +113,8 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev, static int ksz9021_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; - struct device_node *of_node = dev->device_node; + const struct device_d *dev = &phydev->dev; + const struct device_node *of_node = dev->device_node; const char *clk_pad_skew_names[] = { "txen-skew-ps", "txc-skew-ps", "rxdv-skew-ps", "rxc-skew-ps" @@ -149,15 +149,20 @@ static int ksz9021_config_init(struct phy_device *phydev) #define KSZ9031_PS_TO_REG 60 /* Extended registers */ +/* MMD Address 0x0 */ +#define MII_KSZ9031RN_FLP_BURST_TX_LO 3 +#define MII_KSZ9031RN_FLP_BURST_TX_HI 4 + +/* MMD Address 0x2 */ #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 #define MII_KSZ9031RN_CLK_PAD_SKEW 8 static int ksz9031_of_load_skew_values(struct phy_device *phydev, - struct device_node *of_node, + const struct device_node *of_node, u16 reg, size_t field_sz, - char *field[], u8 numfields) + const char *field[], u8 numfields) { int val[4] = {-1, -2, -3, -4}; int matches = 0; @@ -192,20 +197,29 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, return 0; } +static int ksz9031_center_flp_timing(struct phy_device *phydev) +{ + /* Center KSZ9031RNX FLP timing at 16ms. */ + phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_HI, 0, 0x0006); + phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_LO, 0, 0x1a80); + + return genphy_restart_aneg(phydev); +} + static int ksz9031_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; - struct device_node *of_node = dev->device_node; - char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; - char *rx_data_skews[4] = { + const struct device_d *dev = &phydev->dev; + const struct device_node *of_node = dev->device_node; + static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; + static const char *rx_data_skews[4] = { "rxd0-skew-ps", "rxd1-skew-ps", "rxd2-skew-ps", "rxd3-skew-ps" }; - char *tx_data_skews[4] = { + static const char *tx_data_skews[4] = { "txd0-skew-ps", "txd1-skew-ps", "txd2-skew-ps", "txd3-skew-ps" }; - char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; + static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; if (!of_node && dev->parent->device_node) of_node = dev->parent->device_node; @@ -227,13 +241,14 @@ static int ksz9031_config_init(struct phy_device *phydev) MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, tx_data_skews, 4); } - return 0; + + return ksz9031_center_flp_timing(phydev); } #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) +#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) +#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) +static int ksz8873mll_read_status(struct phy_device *phydev) { int regval; @@ -258,6 +273,27 @@ int ksz8873mll_read_status(struct phy_device *phydev) return 0; } +static int ksz9031_read_status(struct phy_device *phydev) +{ + int err; + int regval; + + err = genphy_read_status(phydev); + if (err) + return err; + + /* Make sure the PHY is not broken. Read idle error count, + * and reset the PHY if it is maxed out. + */ + regval = phy_read(phydev, MII_STAT1000); + if ((regval & 0xff) == 0xff) { + phy_init_hw(phydev); + phydev->link = 0; + } + + return 0; +} + static int ksz8873mll_config_aneg(struct phy_device *phydev) { return 0; @@ -353,7 +389,7 @@ static struct phy_driver ksphy_driver[] = { .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), .config_init = ksz9031_config_init, .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, + .read_status = ksz9031_read_status, }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 25c999c550..be2c68bf68 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -147,8 +147,13 @@ int phy_scan_fixups(struct phy_device *phydev) return 0; } - -static struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) +/** + * phy_device_create - creates a struct phy_device. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @phy_id: PHY ID. + */ +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *phydev; @@ -168,8 +173,10 @@ static struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int p phydev->bus = bus; phydev->dev.bus = &mdio_bus_type; - strcpy(phydev->dev.name, "phy"); - phydev->dev.id = DEVICE_ID_DYNAMIC; + sprintf(phydev->dev.name, "mdio%d-phy%02x", + phydev->bus->dev.id, + phydev->addr); + phydev->dev.id = DEVICE_ID_SINGLE; return phydev; } |