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