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