176 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Software PHY emulation4 *5 * Code taken from fixed_phy.c by Russell King.6 *7 * Author: Vitaly Bordug <vbordug@ru.mvista.com>8 * Anton Vorontsov <avorontsov@ru.mvista.com>9 *10 * Copyright (c) 2006-2007 MontaVista Software, Inc.11 */12#include <linux/export.h>13#include <linux/mii.h>14#include <linux/phy.h>15#include <linux/phy_fixed.h>16 17#include "swphy.h"18 19#define MII_REGS_NUM 2920 21struct swmii_regs {22 u16 bmsr;23 u16 lpa;24 u16 lpagb;25 u16 estat;26};27 28enum {29 SWMII_SPEED_10 = 0,30 SWMII_SPEED_100,31 SWMII_SPEED_1000,32 SWMII_DUPLEX_HALF = 0,33 SWMII_DUPLEX_FULL,34};35 36/*37 * These two tables get bitwise-anded together to produce the final result.38 * This means the speed table must contain both duplex settings, and the39 * duplex table must contain all speed settings.40 */41static const struct swmii_regs speed[] = {42 [SWMII_SPEED_10] = {43 .lpa = LPA_10FULL | LPA_10HALF,44 },45 [SWMII_SPEED_100] = {46 .bmsr = BMSR_100FULL | BMSR_100HALF,47 .lpa = LPA_100FULL | LPA_100HALF,48 },49 [SWMII_SPEED_1000] = {50 .bmsr = BMSR_ESTATEN,51 .lpagb = LPA_1000FULL | LPA_1000HALF,52 .estat = ESTATUS_1000_TFULL | ESTATUS_1000_THALF,53 },54};55 56static const struct swmii_regs duplex[] = {57 [SWMII_DUPLEX_HALF] = {58 .bmsr = BMSR_ESTATEN | BMSR_100HALF,59 .lpa = LPA_10HALF | LPA_100HALF,60 .lpagb = LPA_1000HALF,61 .estat = ESTATUS_1000_THALF,62 },63 [SWMII_DUPLEX_FULL] = {64 .bmsr = BMSR_ESTATEN | BMSR_100FULL,65 .lpa = LPA_10FULL | LPA_100FULL,66 .lpagb = LPA_1000FULL,67 .estat = ESTATUS_1000_TFULL,68 },69};70 71static int swphy_decode_speed(int speed)72{73 switch (speed) {74 case 1000:75 return SWMII_SPEED_1000;76 case 100:77 return SWMII_SPEED_100;78 case 10:79 return SWMII_SPEED_10;80 default:81 return -EINVAL;82 }83}84 85/**86 * swphy_validate_state - validate the software phy status87 * @state: software phy status88 *89 * This checks that we can represent the state stored in @state can be90 * represented in the emulated MII registers. Returns 0 if it can,91 * otherwise returns -EINVAL.92 */93int swphy_validate_state(const struct fixed_phy_status *state)94{95 int err;96 97 if (state->link) {98 err = swphy_decode_speed(state->speed);99 if (err < 0) {100 pr_warn("swphy: unknown speed\n");101 return -EINVAL;102 }103 }104 return 0;105}106EXPORT_SYMBOL_GPL(swphy_validate_state);107 108/**109 * swphy_read_reg - return a MII register from the fixed phy state110 * @reg: MII register111 * @state: fixed phy status112 *113 * Return the MII @reg register generated from the fixed phy state @state.114 */115int swphy_read_reg(int reg, const struct fixed_phy_status *state)116{117 int speed_index, duplex_index;118 u16 bmsr = BMSR_ANEGCAPABLE;119 u16 estat = 0;120 u16 lpagb = 0;121 u16 lpa = 0;122 123 if (reg > MII_REGS_NUM)124 return -1;125 126 speed_index = swphy_decode_speed(state->speed);127 if (WARN_ON(speed_index < 0))128 return 0;129 130 duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;131 132 bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr;133 estat |= speed[speed_index].estat & duplex[duplex_index].estat;134 135 if (state->link) {136 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;137 138 lpa |= speed[speed_index].lpa & duplex[duplex_index].lpa;139 lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb;140 141 if (state->pause)142 lpa |= LPA_PAUSE_CAP;143 144 if (state->asym_pause)145 lpa |= LPA_PAUSE_ASYM;146 }147 148 switch (reg) {149 case MII_BMCR:150 return BMCR_ANENABLE;151 case MII_BMSR:152 return bmsr;153 case MII_PHYSID1:154 case MII_PHYSID2:155 return 0;156 case MII_LPA:157 return lpa;158 case MII_STAT1000:159 return lpagb;160 case MII_ESTATUS:161 return estat;162 163 /*164 * We do not support emulating Clause 45 over Clause 22 register165 * reads. Return an error instead of bogus data.166 */167 case MII_MMD_CTRL:168 case MII_MMD_DATA:169 return -1;170 171 default:172 return 0xffff;173 }174}175EXPORT_SYMBOL_GPL(swphy_read_reg);176