diff options
Diffstat (limited to 'drivers/net/phy/swphy.c')
-rw-r--r-- | drivers/net/phy/swphy.c | 179 |
1 files changed, 179 insertions, 0 deletions
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); |