linux/drivers/net/phy/swphy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Software PHY emulation
   4 *
   5 * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk>
   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 29
  20
  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 the
  39 * 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 status
  87 * @state: software phy status
  88 *
  89 * This checks that we can represent the state stored in @state can be
  90 * 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 state
 110 * @reg: MII register
 111 * @state: fixed phy status
 112 *
 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 register
 165         * 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