Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
[linux-2.6-microblaze.git] / drivers / net / phy / swphy.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Software PHY emulation
4  *
5  * Code taken from fixed_phy.c by Russell King.
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         u16 estat;
26 };
27
28 enum {
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  */
41 static 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
56 static 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
71 static 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  */
93 int 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 }
106 EXPORT_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  */
115 int 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 }
175 EXPORT_SYMBOL_GPL(swphy_read_reg);