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