diff options
author | David S. Miller <davem@davemloft.net> | 2016-06-27 10:41:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-06-27 10:41:02 -0400 |
commit | de2fbe7ae3637533ebf711b91b04988633bf38ee (patch) | |
tree | 26af32aadf4e16da795e00c2d598bd8efbff593c | |
parent | 5db15872c5370d9ae7b1d1c972a48b598b23b824 (diff) | |
parent | bf7afb29d545a6875fa44e17ddd23398e3dc30de (diff) | |
download | linux-de2fbe7ae3637533ebf711b91b04988633bf38ee.tar.bz2 |
Merge branch 'sfp-infra'
Russell King says:
====================
Initial SFP support patches
Please review and merge this initial patch set, which is part of a
larger set previously posted adding SFP support to phy and mvneta.
This initial set are focused on cleaning up and reorganising the
fixed-phy code to allow the core software-phy code to be re-used.
These are based on net-next.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/phy/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/phy/fixed_phy.c | 153 | ||||
-rw-r--r-- | drivers/net/phy/swphy.c | 179 | ||||
-rw-r--r-- | drivers/net/phy/swphy.h | 9 |
5 files changed, 222 insertions, 126 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 8dac88abbc39..f96829415ce6 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -12,6 +12,9 @@ menuconfig PHYLIB if PHYLIB +config SWPHY + bool + comment "MII PHY device drivers" config AQUANTIA_PHY @@ -159,6 +162,7 @@ config MICROCHIP_PHY config FIXED_PHY tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" depends on PHYLIB + select SWPHY ---help--- Adds the platform "fixed" MDIO Bus to cover the boards that use PHYs that are not connected to the real MDIO bus. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 4170642a2035..7158274327d0 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,6 +1,7 @@ # Makefile for Linux PHY drivers -libphy-objs := phy.o phy_device.o mdio_bus.o mdio_device.o +libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o +libphy-$(CONFIG_SWPHY) += swphy.o obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 2d2e4339f0df..b376ada83598 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -23,8 +23,9 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/gpio.h> +#include <linux/seqlock.h> -#define MII_REGS_NUM 29 +#include "swphy.h" struct fixed_mdio_bus { struct mii_bus *mii_bus; @@ -33,8 +34,8 @@ struct fixed_mdio_bus { struct fixed_phy { int addr; - u16 regs[MII_REGS_NUM]; struct phy_device *phydev; + seqcount_t seqcount; struct fixed_phy_status status; int (*link_update)(struct net_device *, struct fixed_phy_status *); struct list_head node; @@ -46,103 +47,10 @@ static struct fixed_mdio_bus platform_fmb = { .phys = LIST_HEAD_INIT(platform_fmb.phys), }; -static int fixed_phy_update_regs(struct fixed_phy *fp) +static void fixed_phy_update(struct fixed_phy *fp) { - u16 bmsr = BMSR_ANEGCAPABLE; - u16 bmcr = 0; - u16 lpagb = 0; - u16 lpa = 0; - if (gpio_is_valid(fp->link_gpio)) fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio); - - if (fp->status.duplex) { - switch (fp->status.speed) { - case 1000: - bmsr |= BMSR_ESTATEN; - break; - case 100: - bmsr |= BMSR_100FULL; - break; - case 10: - bmsr |= BMSR_10FULL; - break; - default: - break; - } - } else { - switch (fp->status.speed) { - case 1000: - bmsr |= BMSR_ESTATEN; - break; - case 100: - bmsr |= BMSR_100HALF; - break; - case 10: - bmsr |= BMSR_10HALF; - break; - default: - break; - } - } - - if (fp->status.link) { - bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; - - if (fp->status.duplex) { - bmcr |= BMCR_FULLDPLX; - - switch (fp->status.speed) { - case 1000: - bmcr |= BMCR_SPEED1000; - lpagb |= LPA_1000FULL; - break; - case 100: - bmcr |= BMCR_SPEED100; - lpa |= LPA_100FULL; - break; - case 10: - lpa |= LPA_10FULL; - break; - default: - pr_warn("fixed phy: unknown speed\n"); - return -EINVAL; - } - } else { - switch (fp->status.speed) { - case 1000: - bmcr |= BMCR_SPEED1000; - lpagb |= LPA_1000HALF; - break; - case 100: - bmcr |= BMCR_SPEED100; - lpa |= LPA_100HALF; - break; - case 10: - lpa |= LPA_10HALF; - break; - default: - pr_warn("fixed phy: unknown speed\n"); - return -EINVAL; - } - } - - if (fp->status.pause) - lpa |= LPA_PAUSE_CAP; - - if (fp->status.asym_pause) - lpa |= LPA_PAUSE_ASYM; - } - - fp->regs[MII_PHYSID1] = 0; - fp->regs[MII_PHYSID2] = 0; - - fp->regs[MII_BMSR] = bmsr; - fp->regs[MII_BMCR] = bmcr; - fp->regs[MII_LPA] = lpa; - fp->regs[MII_STAT1000] = lpagb; - - return 0; } static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) @@ -150,29 +58,23 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) struct fixed_mdio_bus *fmb = bus->priv; struct fixed_phy *fp; - if (reg_num >= MII_REGS_NUM) - return -1; - - /* We do not support emulating Clause 45 over Clause 22 register reads - * return an error instead of bogus data. - */ - switch (reg_num) { - case MII_MMD_CTRL: - case MII_MMD_DATA: - return -1; - default: - break; - } - list_for_each_entry(fp, &fmb->phys, node) { if (fp->addr == phy_addr) { - /* Issue callback if user registered it. */ - if (fp->link_update) { - fp->link_update(fp->phydev->attached_dev, - &fp->status); - fixed_phy_update_regs(fp); - } - return fp->regs[reg_num]; + struct fixed_phy_status state; + int s; + + do { + s = read_seqcount_begin(&fp->seqcount); + /* Issue callback if user registered it. */ + if (fp->link_update) { + fp->link_update(fp->phydev->attached_dev, + &fp->status); + fixed_phy_update(fp); + } + state = fp->status; + } while (read_seqcount_retry(&fp->seqcount, s)); + + return swphy_read_reg(reg_num, &state); } } @@ -224,6 +126,7 @@ int fixed_phy_update_state(struct phy_device *phydev, list_for_each_entry(fp, &fmb->phys, node) { if (fp->addr == phydev->mdio.addr) { + write_seqcount_begin(&fp->seqcount); #define _UPD(x) if (changed->x) \ fp->status.x = status->x _UPD(link); @@ -232,7 +135,8 @@ int fixed_phy_update_state(struct phy_device *phydev, _UPD(pause); _UPD(asym_pause); #undef _UPD - fixed_phy_update_regs(fp); + fixed_phy_update(fp); + write_seqcount_end(&fp->seqcount); return 0; } } @@ -249,11 +153,15 @@ int fixed_phy_add(unsigned int irq, int phy_addr, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; + ret = swphy_validate_state(status); + if (ret < 0) + return ret; + fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) return -ENOMEM; - memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); + seqcount_init(&fp->seqcount); if (irq != PHY_POLL) fmb->mii_bus->irq[phy_addr] = irq; @@ -269,17 +177,12 @@ int fixed_phy_add(unsigned int irq, int phy_addr, goto err_regs; } - ret = fixed_phy_update_regs(fp); - if (ret) - goto err_gpio; + fixed_phy_update(fp); list_add_tail(&fp->node, &fmb->phys); return 0; -err_gpio: - if (gpio_is_valid(fp->link_gpio)) - gpio_free(fp->link_gpio); err_regs: kfree(fp); return ret; diff --git a/drivers/net/phy/swphy.c b/drivers/net/phy/swphy.c new file mode 100644 index 000000000000..34f58f2349e9 --- /dev/null +++ b/drivers/net/phy/swphy.c @@ -0,0 +1,179 @@ +/* + * Software PHY emulation + * + * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk> + * + * Author: Vitaly Bordug <vbordug@ru.mvista.com> + * Anton Vorontsov <avorontsov@ru.mvista.com> + * + * Copyright (c) 2006-2007 MontaVista Software, 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. + */ +#include <linux/export.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/phy_fixed.h> + +#include "swphy.h" + +#define MII_REGS_NUM 29 + +struct swmii_regs { + u16 bmcr; + u16 bmsr; + u16 lpa; + u16 lpagb; +}; + +enum { + SWMII_SPEED_10 = 0, + SWMII_SPEED_100, + SWMII_SPEED_1000, + SWMII_DUPLEX_HALF = 0, + SWMII_DUPLEX_FULL, +}; + +/* + * These two tables get bitwise-anded together to produce the final result. + * This means the speed table must contain both duplex settings, and the + * duplex table must contain all speed settings. + */ +static const struct swmii_regs speed[] = { + [SWMII_SPEED_10] = { + .bmcr = BMCR_FULLDPLX, + .lpa = LPA_10FULL | LPA_10HALF, + }, + [SWMII_SPEED_100] = { + .bmcr = BMCR_FULLDPLX | BMCR_SPEED100, + .bmsr = BMSR_100FULL | BMSR_100HALF, + .lpa = LPA_100FULL | LPA_100HALF, + }, + [SWMII_SPEED_1000] = { + .bmcr = BMCR_FULLDPLX | BMCR_SPEED1000, + .bmsr = BMSR_ESTATEN, + .lpagb = LPA_1000FULL | LPA_1000HALF, + }, +}; + +static const struct swmii_regs duplex[] = { + [SWMII_DUPLEX_HALF] = { + .bmcr = ~BMCR_FULLDPLX, + .bmsr = BMSR_ESTATEN | BMSR_100HALF, + .lpa = LPA_10HALF | LPA_100HALF, + .lpagb = LPA_1000HALF, + }, + [SWMII_DUPLEX_FULL] = { + .bmcr = ~0, + .bmsr = BMSR_ESTATEN | BMSR_100FULL, + .lpa = LPA_10FULL | LPA_100FULL, + .lpagb = LPA_1000FULL, + }, +}; + +static int swphy_decode_speed(int speed) +{ + switch (speed) { + case 1000: + return SWMII_SPEED_1000; + case 100: + return SWMII_SPEED_100; + case 10: + return SWMII_SPEED_10; + default: + return -EINVAL; + } +} + +/** + * swphy_validate_state - validate the software phy status + * @state: software phy status + * + * This checks that we can represent the state stored in @state can be + * represented in the emulated MII registers. Returns 0 if it can, + * otherwise returns -EINVAL. + */ +int swphy_validate_state(const struct fixed_phy_status *state) +{ + int err; + + if (state->link) { + err = swphy_decode_speed(state->speed); + if (err < 0) { + pr_warn("swphy: unknown speed\n"); + return -EINVAL; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(swphy_validate_state); + +/** + * swphy_read_reg - return a MII register from the fixed phy state + * @reg: MII register + * @state: fixed phy status + * + * Return the MII @reg register generated from the fixed phy state @state. + */ +int swphy_read_reg(int reg, const struct fixed_phy_status *state) +{ + int speed_index, duplex_index; + u16 bmsr = BMSR_ANEGCAPABLE; + u16 bmcr = 0; + u16 lpagb = 0; + u16 lpa = 0; + + if (reg > MII_REGS_NUM) + return -1; + + speed_index = swphy_decode_speed(state->speed); + if (WARN_ON(speed_index < 0)) + return 0; + + duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF; + + bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr; + + if (state->link) { + bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; + + bmcr |= speed[speed_index].bmcr & duplex[duplex_index].bmcr; + lpa |= speed[speed_index].lpa & duplex[duplex_index].lpa; + lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb; + + if (state->pause) + lpa |= LPA_PAUSE_CAP; + + if (state->asym_pause) + lpa |= LPA_PAUSE_ASYM; + } + + switch (reg) { + case MII_BMCR: + return bmcr; + case MII_BMSR: + return bmsr; + case MII_PHYSID1: + case MII_PHYSID2: + return 0; + case MII_LPA: + return lpa; + case MII_STAT1000: + return lpagb; + + /* + * We do not support emulating Clause 45 over Clause 22 register + * reads. Return an error instead of bogus data. + */ + case MII_MMD_CTRL: + case MII_MMD_DATA: + return -1; + + default: + return 0xffff; + } +} +EXPORT_SYMBOL_GPL(swphy_read_reg); diff --git a/drivers/net/phy/swphy.h b/drivers/net/phy/swphy.h new file mode 100644 index 000000000000..2f09ac324e18 --- /dev/null +++ b/drivers/net/phy/swphy.h @@ -0,0 +1,9 @@ +#ifndef SWPHY_H +#define SWPHY_H + +struct fixed_phy_status; + +int swphy_validate_state(const struct fixed_phy_status *state); +int swphy_read_reg(int reg, const struct fixed_phy_status *state); + +#endif |