3
3
4
4
#include <linux/kernel.h>
5
5
#include <linux/module.h>
6
+ #include <linux/delay.h>
6
7
#include <linux/mii.h>
7
8
#include <linux/phy.h>
8
9
10
+ /* External Register Control Register */
11
+ #define LAN87XX_EXT_REG_CTL (0x14)
12
+ #define LAN87XX_EXT_REG_CTL_RD_CTL (0x1000)
13
+ #define LAN87XX_EXT_REG_CTL_WR_CTL (0x0800)
14
+
15
+ /* External Register Read Data Register */
16
+ #define LAN87XX_EXT_REG_RD_DATA (0x15)
17
+
18
+ /* External Register Write Data Register */
19
+ #define LAN87XX_EXT_REG_WR_DATA (0x16)
20
+
9
21
/* Interrupt Source Register */
10
22
#define LAN87XX_INTERRUPT_SOURCE (0x18)
11
23
14
26
#define LAN87XX_MASK_LINK_UP (0x0004)
15
27
#define LAN87XX_MASK_LINK_DOWN (0x0002)
16
28
29
+ /* phyaccess nested types */
30
+ #define PHYACC_ATTR_MODE_READ 0
31
+ #define PHYACC_ATTR_MODE_WRITE 1
32
+ #define PHYACC_ATTR_MODE_MODIFY 2
33
+
34
+ #define PHYACC_ATTR_BANK_SMI 0
35
+ #define PHYACC_ATTR_BANK_MISC 1
36
+ #define PHYACC_ATTR_BANK_PCS 2
37
+ #define PHYACC_ATTR_BANK_AFE 3
38
+ #define PHYACC_ATTR_BANK_MAX 7
39
+
17
40
#define DRIVER_AUTHOR "Nisar Sayed <
[email protected] >"
18
41
#define DRIVER_DESC "Microchip LAN87XX T1 PHY driver"
19
42
43
+ struct access_ereg_val {
44
+ u8 mode ;
45
+ u8 bank ;
46
+ u8 offset ;
47
+ u16 val ;
48
+ u16 mask ;
49
+ };
50
+
51
+ static int access_ereg (struct phy_device * phydev , u8 mode , u8 bank ,
52
+ u8 offset , u16 val )
53
+ {
54
+ u16 ereg = 0 ;
55
+ int rc = 0 ;
56
+
57
+ if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX )
58
+ return - EINVAL ;
59
+
60
+ if (bank == PHYACC_ATTR_BANK_SMI ) {
61
+ if (mode == PHYACC_ATTR_MODE_WRITE )
62
+ rc = phy_write (phydev , offset , val );
63
+ else
64
+ rc = phy_read (phydev , offset );
65
+ return rc ;
66
+ }
67
+
68
+ if (mode == PHYACC_ATTR_MODE_WRITE ) {
69
+ ereg = LAN87XX_EXT_REG_CTL_WR_CTL ;
70
+ rc = phy_write (phydev , LAN87XX_EXT_REG_WR_DATA , val );
71
+ if (rc < 0 )
72
+ return rc ;
73
+ } else {
74
+ ereg = LAN87XX_EXT_REG_CTL_RD_CTL ;
75
+ }
76
+
77
+ ereg |= (bank << 8 ) | offset ;
78
+
79
+ rc = phy_write (phydev , LAN87XX_EXT_REG_CTL , ereg );
80
+ if (rc < 0 )
81
+ return rc ;
82
+
83
+ if (mode == PHYACC_ATTR_MODE_READ )
84
+ rc = phy_read (phydev , LAN87XX_EXT_REG_RD_DATA );
85
+
86
+ return rc ;
87
+ }
88
+
89
+ static int access_ereg_modify_changed (struct phy_device * phydev ,
90
+ u8 bank , u8 offset , u16 val , u16 mask )
91
+ {
92
+ int new = 0 , rc = 0 ;
93
+
94
+ if (bank > PHYACC_ATTR_BANK_MAX )
95
+ return - EINVAL ;
96
+
97
+ rc = access_ereg (phydev , PHYACC_ATTR_MODE_READ , bank , offset , val );
98
+ if (rc < 0 )
99
+ return rc ;
100
+
101
+ new = val | (rc & (mask ^ 0xFFFF ));
102
+ rc = access_ereg (phydev , PHYACC_ATTR_MODE_WRITE , bank , offset , new );
103
+
104
+ return rc ;
105
+ }
106
+
107
+ static int lan87xx_phy_init (struct phy_device * phydev )
108
+ {
109
+ static const struct access_ereg_val init [] = {
110
+ /* TX Amplitude = 5 */
111
+ {PHYACC_ATTR_MODE_MODIFY , PHYACC_ATTR_BANK_AFE , 0x0B ,
112
+ 0x000A , 0x001E },
113
+ /* Clear SMI interrupts */
114
+ {PHYACC_ATTR_MODE_READ , PHYACC_ATTR_BANK_SMI , 0x18 ,
115
+ 0 , 0 },
116
+ /* Clear MISC interrupts */
117
+ {PHYACC_ATTR_MODE_READ , PHYACC_ATTR_BANK_MISC , 0x08 ,
118
+ 0 , 0 },
119
+ /* Turn on TC10 Ring Oscillator (ROSC) */
120
+ {PHYACC_ATTR_MODE_MODIFY , PHYACC_ATTR_BANK_MISC , 0x20 ,
121
+ 0x0020 , 0x0020 },
122
+ /* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */
123
+ {PHYACC_ATTR_MODE_WRITE , PHYACC_ATTR_BANK_PCS , 0x20 ,
124
+ 0x283C , 0 },
125
+ /* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */
126
+ {PHYACC_ATTR_MODE_WRITE , PHYACC_ATTR_BANK_MISC , 0x21 ,
127
+ 0x274F , 0 },
128
+ /* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep,
129
+ * and Wake_In to wake PHY
130
+ */
131
+ {PHYACC_ATTR_MODE_WRITE , PHYACC_ATTR_BANK_MISC , 0x20 ,
132
+ 0x80A7 , 0 },
133
+ /* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer
134
+ * to 128 uS
135
+ */
136
+ {PHYACC_ATTR_MODE_WRITE , PHYACC_ATTR_BANK_MISC , 0x24 ,
137
+ 0xF110 , 0 },
138
+ /* Enable HW Init */
139
+ {PHYACC_ATTR_MODE_MODIFY , PHYACC_ATTR_BANK_SMI , 0x1A ,
140
+ 0x0100 , 0x0100 },
141
+ };
142
+ int rc , i ;
143
+
144
+ /* Start manual initialization procedures in Managed Mode */
145
+ rc = access_ereg_modify_changed (phydev , PHYACC_ATTR_BANK_SMI ,
146
+ 0x1a , 0x0000 , 0x0100 );
147
+ if (rc < 0 )
148
+ return rc ;
149
+
150
+ /* Soft Reset the SMI block */
151
+ rc = access_ereg_modify_changed (phydev , PHYACC_ATTR_BANK_SMI ,
152
+ 0x00 , 0x8000 , 0x8000 );
153
+ if (rc < 0 )
154
+ return rc ;
155
+
156
+ /* Check to see if the self-clearing bit is cleared */
157
+ usleep_range (1000 , 2000 );
158
+ rc = access_ereg (phydev , PHYACC_ATTR_MODE_READ ,
159
+ PHYACC_ATTR_BANK_SMI , 0x00 , 0 );
160
+ if (rc < 0 )
161
+ return rc ;
162
+ if ((rc & 0x8000 ) != 0 )
163
+ return - ETIMEDOUT ;
164
+
165
+ /* PHY Initialization */
166
+ for (i = 0 ; i < ARRAY_SIZE (init ); i ++ ) {
167
+ if (init [i ].mode == PHYACC_ATTR_MODE_MODIFY ) {
168
+ rc = access_ereg_modify_changed (phydev , init [i ].bank ,
169
+ init [i ].offset ,
170
+ init [i ].val ,
171
+ init [i ].mask );
172
+ } else {
173
+ rc = access_ereg (phydev , init [i ].mode , init [i ].bank ,
174
+ init [i ].offset , init [i ].val );
175
+ }
176
+ if (rc < 0 )
177
+ return rc ;
178
+ }
179
+
180
+ return 0 ;
181
+ }
182
+
20
183
static int lan87xx_phy_config_intr (struct phy_device * phydev )
21
184
{
22
185
int rc , val = 0 ;
@@ -40,6 +203,13 @@ static int lan87xx_phy_ack_interrupt(struct phy_device *phydev)
40
203
return rc < 0 ? rc : 0 ;
41
204
}
42
205
206
+ static int lan87xx_config_init (struct phy_device * phydev )
207
+ {
208
+ int rc = lan87xx_phy_init (phydev );
209
+
210
+ return rc < 0 ? rc : 0 ;
211
+ }
212
+
43
213
static struct phy_driver microchip_t1_phy_driver [] = {
44
214
{
45
215
.phy_id = 0x0007c150 ,
@@ -48,6 +218,7 @@ static struct phy_driver microchip_t1_phy_driver[] = {
48
218
49
219
.features = PHY_BASIC_T1_FEATURES ,
50
220
221
+ .config_init = lan87xx_config_init ,
51
222
.config_aneg = genphy_config_aneg ,
52
223
53
224
.ack_interrupt = lan87xx_phy_ack_interrupt ,
0 commit comments