Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | // SPDX-License-Identifier: GPL-2.0+ /* * Software PHY emulation * * Code taken from fixed_phy.c by Russell King. * * Author: Vitaly Bordug <vbordug@ru.mvista.com> * Anton Vorontsov <avorontsov@ru.mvista.com> * * Copyright (c) 2006-2007 MontaVista Software, Inc. */ #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 bmsr; u16 lpa; u16 lpagb; u16 estat; }; 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] = { .lpa = LPA_10FULL | LPA_10HALF, }, [SWMII_SPEED_100] = { .bmsr = BMSR_100FULL | BMSR_100HALF, .lpa = LPA_100FULL | LPA_100HALF, }, [SWMII_SPEED_1000] = { .bmsr = BMSR_ESTATEN, .lpagb = LPA_1000FULL | LPA_1000HALF, .estat = ESTATUS_1000_TFULL | ESTATUS_1000_THALF, }, }; static const struct swmii_regs duplex[] = { [SWMII_DUPLEX_HALF] = { .bmsr = BMSR_ESTATEN | BMSR_100HALF, .lpa = LPA_10HALF | LPA_100HALF, .lpagb = LPA_1000HALF, .estat = ESTATUS_1000_THALF, }, [SWMII_DUPLEX_FULL] = { .bmsr = BMSR_ESTATEN | BMSR_100FULL, .lpa = LPA_10FULL | LPA_100FULL, .lpagb = LPA_1000FULL, .estat = ESTATUS_1000_TFULL, }, }; 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 estat = 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; estat |= speed[speed_index].estat & duplex[duplex_index].estat; if (state->link) { bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; 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_ANENABLE; case MII_BMSR: return bmsr; case MII_PHYSID1: case MII_PHYSID2: return 0; case MII_LPA: return lpa; case MII_STAT1000: return lpagb; case MII_ESTATUS: return estat; /* * 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); |