diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst index 39084e5..5b021f0 100644 --- a/Documentation/user/booting-linux.rst +++ b/Documentation/user/booting-linux.rst @@ -203,7 +203,7 @@ .. code-block:: sh - boot nfs://nfshost//path/ + boot nfs://nfshost[:port]//path/ Additionally to the options defined in the original spec barebox understands the ``linux-appendroot`` option. This is a boolean value and if set to ``true`` barebox diff --git a/commands/miitool.c b/commands/miitool.c index c62e758..ba6e604 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -115,8 +115,9 @@ for (i = 0; i < 32; i++) mii_val[i] = mii->read(mii, phydev->addr, i); - printf("%s: %s%d: ", phydev->cdev.name, - mii->parent->name, mii->parent->id); + printf((mii->parent->id) < 0 ? "%s: %s:" : "%s: %s%d: ", + phydev->cdev.name, mii->parent->name, mii->parent->id); + if (mii_val[MII_BMCR] == 0xffff || mii_val[MII_BMSR] == 0x0000) { fprintf(stderr, " No MII transceiver present!.\n"); @@ -233,70 +234,137 @@ struct phy_device *phydev; phydev = mdiobus_scan(mii, i); - if (IS_ERR(phydev)) + if (IS_ERR(phydev) || !phydev->registered) continue; - if (phydev->registered) { - show_basic_mii(mii, phydev, verbose); + /* + * If we are looking for a secific phy, called + * 'phydevname', but current phydev is not it, skip to + * the next iteration + */ + if (phydevname && + strcmp(phydev->cdev.name, phydevname)) + continue; - if (phydevname && - !strcmp(phydev->cdev.name, phydevname)) { - return; - } - } + show_basic_mii(mii, phydev, verbose); + /* + * We were looking for a specific device and at this + * point we already shown the info about it so end the + * loop and exit + */ + if (phydevname) + break; } return; } +enum miitool_operations { + MIITOOL_NOOP, + MIITOOL_SHOW, + MIITOOL_REGISTER, +}; + static int do_miitool(int argc, char *argv[]) { - char *phydevname; + char *phydevname = NULL; + char *regstr = NULL; + char *endp; struct mii_bus *mii; - int opt; - int argc_min; - int verbose; + int opt, ret; + int verbose = 0; + struct phy_device *phydev; + enum miitool_operations action = MIITOOL_NOOP; + int addr, bus; - verbose = 0; - while ((opt = getopt(argc, argv, "v")) > 0) { + while ((opt = getopt(argc, argv, "vs:r:")) > 0) { switch (opt) { + case 'a': + addr = simple_strtol(optarg, NULL, 0); + break; + case 'b': + bus = simple_strtoul(optarg, NULL, 0); + break; + case 's': + action = MIITOOL_SHOW; + phydevname = xstrdup(optarg); + break; + case 'r': + action = MIITOOL_REGISTER; + regstr = optarg; + break; case 'v': verbose++; break; default: - return COMMAND_ERROR_USAGE; + ret = COMMAND_ERROR_USAGE; + goto free_phydevname; } } - argc_min = optind + 1; + switch (action) { + case MIITOOL_REGISTER: + bus = simple_strtoul(regstr, &endp, 0); + if (*endp != ':') { + printf("No colon between bus and address\n"); + return COMMAND_ERROR_USAGE; + } + endp++; + addr = simple_strtoul(endp, NULL, 0); - phydevname = NULL; - if (argc >= argc_min) { - phydevname = argv[optind]; + if (addr >= PHY_MAX_ADDR) + printf("Address out of range (max %d)\n", PHY_MAX_ADDR - 1); + + mii = mdiobus_get_bus(bus); + if (!mii) { + printf("Can't find MDIO bus #%d\n", bus); + ret = COMMAND_ERROR; + goto free_phydevname; + } + + phydev = phy_device_create(mii, addr, -1); + ret = phy_register_device(phydev); + if (ret) { + printf("failed to register phy %s: %s\n", + dev_name(&phydev->dev), strerror(-ret)); + goto free_phydevname; + } else { + printf("registered phy %s\n", dev_name(&phydev->dev)); + } + break; + default: + case MIITOOL_SHOW: + for_each_mii_bus(mii) { + mdiobus_detect(&mii->dev); + mdiobus_show(&mii->dev, phydevname, verbose); + } + break; } - for_each_mii_bus(mii) { - mdiobus_detect(&mii->dev); - mdiobus_show(&mii->dev, phydevname, verbose); - } + ret = COMMAND_SUCCESS; - return COMMAND_SUCCESS; +free_phydevname: + free(phydevname); + return ret; } BAREBOX_CMD_HELP_START(miitool) BAREBOX_CMD_HELP_TEXT("This utility checks or sets the status of a network interface's") -BAREBOX_CMD_HELP_TEXT("Media Independent Interface (MII) unit. Most fast ethernet") +BAREBOX_CMD_HELP_TEXT("Media Independent Interface (MII) unit as well as allowing to") +BAREBOX_CMD_HELP_TEXT("register dummy PHY devices for raw MDIO access. Most fast ethernet") BAREBOX_CMD_HELP_TEXT("adapters use an MII to autonegotiate link speed and duplex setting.") BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Options:") BAREBOX_CMD_HELP_OPT("-v", "increase verbosity") +BAREBOX_CMD_HELP_OPT("-s ", "show PHY status (not providing PHY prints status of all)") +BAREBOX_CMD_HELP_OPT("-r :", "register a PHY") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(miitool) .cmd = do_miitool, BAREBOX_CMD_DESC("view media-independent interface status") - BAREBOX_CMD_OPTS("[-v] PHY") + BAREBOX_CMD_OPTS("[-vsr]") BAREBOX_CMD_GROUP(CMD_GRP_NET) BAREBOX_CMD_HELP(cmd_miitool_help) BAREBOX_CMD_END diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d0a02c1..d30f65b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -46,6 +46,21 @@ ---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 94b9be8..10732f8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -7,3 +7,5 @@ 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 0000000..4f610e3 --- /dev/null +++ b/drivers/net/phy/mdio-bitbang.c @@ -0,0 +1,228 @@ +/* + * Bitbanged MDIO support. + * + * Author: Scott Wood + * Copyright (c) 2007 Freescale Semiconductor + * + * Based on CPM2 MDIO code which is: + * + * Copyright (c) 2003 Intracom S.A. + * by Pantelis Antoniou + * + * 2005 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * 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 +#include +#include +#include + +#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 0000000..a839f2d --- /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 + * + * based on mvmdio driver from Linux + * Copyright (c) 2008 CSE Semaphore Belgium. + * by Laurent Pinchart + * + * Copyright (C) 2008, Paulius Zaleckas + * + * Based on earlier work by + * + * Copyright (c) 2003 Intracom S.A. + * by Pantelis Antoniou + * + * 2005 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 0959c45..41bf018 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -43,7 +43,7 @@ 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 @@ } 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 @@ 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 095563a..e8a566d 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 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_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 @@ #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 @@ 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 @@ 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 @@ 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 @@ .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 25c999c..be2c68b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -147,8 +147,13 @@ 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 @@ 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; } diff --git a/fs/nfs.c b/fs/nfs.c index 3824752..87828fc 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -133,7 +133,9 @@ IPaddr_t server; char *path; uint16_t mount_port; + unsigned manual_mount_port:1; uint16_t nfs_port; + unsigned manual_nfs_port:1; uint32_t rpc_id; uint32_t rootfh_len; char rootfh[NFS3_FHSIZE]; @@ -1311,7 +1313,7 @@ static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device_d *fsdev) { - char *str; + char *str, *tmp; const char *ip; ip = ip_to_string(npriv->server); @@ -1319,6 +1321,19 @@ ip, npriv->path, rootnfsopts[0] ? "," : "", rootnfsopts); + /* forward specific mount options on demand */ + if (npriv->manual_nfs_port == 1) { + tmp = asprintf("%s,port=%hu", str, npriv->nfs_port); + free(str); + str = tmp; + } + + if (npriv->manual_mount_port == 1) { + tmp = asprintf("%s,mountport=%hu", str, npriv->mount_port); + free(str); + str = tmp; + } + fsdev_set_linux_rootarg(fsdev, str); free(str); @@ -1367,6 +1382,8 @@ goto err2; } npriv->mount_port = ret; + } else { + npriv->manual_mount_port = 1; } debug("mount port: %hu\n", npriv->mount_port); @@ -1378,6 +1395,8 @@ goto err2; } npriv->nfs_port = ret; + } else { + npriv->manual_nfs_port = 1; } debug("nfs port: %d\n", npriv->nfs_port); diff --git a/include/linux/mdio-bitbang.h b/include/linux/mdio-bitbang.h new file mode 100644 index 0000000..76f52bb --- /dev/null +++ b/include/linux/mdio-bitbang.h @@ -0,0 +1,45 @@ +#ifndef __LINUX_MDIO_BITBANG_H +#define __LINUX_MDIO_BITBANG_H + +#include + +struct module; + +struct mdiobb_ctrl; + +struct mdiobb_ops { + struct module *owner; + + /* Set the Management Data Clock high if level is one, + * low if level is zero. + */ + void (*set_mdc)(struct mdiobb_ctrl *ctrl, int level); + + /* Configure the Management Data I/O pin as an input if + * "output" is zero, or an output if "output" is one. + */ + void (*set_mdio_dir)(struct mdiobb_ctrl *ctrl, int output); + + /* Set the Management Data I/O pin high if value is one, + * low if "value" is zero. This may only be called + * when the MDIO pin is configured as an output. + */ + void (*set_mdio_data)(struct mdiobb_ctrl *ctrl, int value); + + /* Retrieve the state Management Data I/O pin. */ + int (*get_mdio_data)(struct mdiobb_ctrl *ctrl); +}; + +struct mdiobb_ctrl { + const struct mdiobb_ops *ops; + /* reset callback */ + int (*reset)(struct mii_bus *bus); +}; + +/* The returned bus is not yet registered with the phy layer. */ +struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl); + +/* The bus must already have been unregistered. */ +void free_mdio_bitbang(struct mii_bus *bus); + +#endif diff --git a/include/linux/phy.h b/include/linux/phy.h index a64b9b5..38b0670 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -69,6 +69,10 @@ */ #define MII_BUS_ID_SIZE (20 - 3) +/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit + IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ +#define MII_ADDR_C45 (1<<30) + #define PHYLIB_FORCE_10 (1 << 0) #define PHYLIB_FORCE_100 (1 << 1) #define PHYLIB_FORCE_LINK (1 << 2) @@ -110,6 +114,8 @@ #define for_each_mii_bus(mii) \ list_for_each_entry(mii, &mii_bus_list, list) +struct mii_bus *mdiobus_get_bus(int busnum); + /** * mdiobus_read - Convenience function for reading a given MII mgmt register * @bus: the mii_bus struct @@ -265,6 +271,7 @@ int phy_init(void); int phy_init_hw(struct phy_device *phydev); +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id); int phy_register_device(struct phy_device* dev); void phy_unregister_device(struct phy_device *phydev);