forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
3.7 KiB
175 lines
3.7 KiB
// SPDX-License-Identifier: GPL-2.0+ |
|
/* |
|
* Software PHY emulation |
|
* |
|
* Code taken from fixed_phy.c by Russell King. |
|
* |
|
* Author: Vitaly Bordug <[email protected]> |
|
* Anton Vorontsov <[email protected]> |
|
* |
|
* 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);
|
|
|