4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include "dmfe_impl.h"
29 * The bit-twiddling required by the MII interface makes the functions
30 * in this file relatively slow, so they should probably only be called
31 * from base/low-pri code. However, there's nothing here that really
32 * won't work at hi-pri, AFAIK; and 'relatively slow' only means that
33 * they have microsecond busy-waits all over the place.
36 static const int mii_reg_size
= 16; /* bits */
39 * ======== Low-level SROM access ========
43 * EEPROM access is here because it shares register functionality with MII.
44 * NB: <romaddr> is a byte address but must be 16-bit aligned.
45 * <cnt> is a byte count, and must be a multiple of 2.
48 dmfe_read_eeprom(dmfe_t
*dmfep
, uint16_t raddr
, uint8_t *ptr
, int cnt
)
53 /* only a whole number of words for now */
54 ASSERT((cnt
% 2) == 0);
55 ASSERT((raddr
% 2) == 0);
57 ASSERT(((raddr
+ cnt
) / 2) < (HIGH_ADDRESS_BIT
<< 1));
59 raddr
/= 2; /* make it a word address */
61 /* loop over multiple words... rom access in 16-bit increments */
64 /* select the eeprom */
65 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM
);
67 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM_CS
);
69 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM_CS
| SEL_CLK
);
71 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM_CS
);
74 /* send 3 bit read command */
75 for (bit
= HIGH_CMD_BIT
; bit
!= 0; bit
>>= 1) {
77 value
= (bit
& EEPROM_READ_CMD
) ? DATA_IN
: 0;
79 /* strobe the bit in */
80 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
81 READ_EEPROM_CS
| value
);
83 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
84 READ_EEPROM_CS
| SEL_CLK
| value
);
86 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
87 READ_EEPROM_CS
| value
);
91 /* send 6 bit address */
92 for (bit
= HIGH_ADDRESS_BIT
; bit
!= 0; bit
>>= 1) {
93 value
= (bit
& raddr
) ? DATA_IN
: 0;
95 /* strobe the bit in */
96 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
97 READ_EEPROM_CS
| value
);
99 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
100 READ_EEPROM_CS
| SEL_CLK
| value
);
102 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
103 READ_EEPROM_CS
| value
);
109 for (bit
= HIGH_DATA_BIT
; bit
!= 0; bit
>>= 1) {
111 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
112 READ_EEPROM_CS
| SEL_CLK
);
115 if (dmfe_chip_get32(dmfep
, ETHER_ROM_REG
) & DATA_OUT
)
119 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM_CS
);
123 /* turn off EEPROM access */
124 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, READ_EEPROM
);
127 /* this makes it endian neutral */
128 *ptr
++ = value
& 0xff;
129 *ptr
++ = (value
>> 8);
137 * ======== Lowest-level bit-twiddling to drive MII interface ========
141 * Poke <nbits> (up to 32) bits from <mii_data> along the MII control lines.
142 * Note: the data is taken starting with the MSB of <mii_data> and working
143 * down through progressively less significant bits.
146 dmfe_poke_mii(dmfe_t
*dmfep
, uint32_t mii_data
, uint_t nbits
)
150 ASSERT(mutex_owned(dmfep
->milock
));
152 for (; nbits
> 0; mii_data
<<= 1, --nbits
) {
154 * Extract the MSB of <mii_data> and shift it to the
155 * proper bit position in the MII-poking register
157 dbit
= mii_data
>> 31;
158 dbit
<<= MII_DATA_OUT_SHIFT
;
159 ASSERT((dbit
& ~MII_DATA_OUT
) == 0);
162 * Drive the bit across the wire ...
164 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
165 MII_WRITE
| dbit
); /* Clock Low */
166 drv_usecwait(MII_DELAY
);
167 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
168 MII_WRITE
| MII_CLOCK
| dbit
); /* Clock High */
169 drv_usecwait(MII_DELAY
);
172 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
,
173 MII_WRITE
| dbit
); /* Clock Low */
174 drv_usecwait(MII_DELAY
);
178 * Put the MDIO port in tri-state for the turn around bits
179 * in MII read and at end of MII management sequence.
182 dmfe_tristate_mii(dmfe_t
*dmfep
)
184 ASSERT(mutex_owned(dmfep
->milock
));
186 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, MII_TRISTATE
);
187 drv_usecwait(MII_DELAY
);
188 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, MII_TRISTATE
| MII_CLOCK
);
189 drv_usecwait(MII_DELAY
);
194 * ======== Next level: issue an MII access command/get a response ========
198 dmfe_mii_command(dmfe_t
*dmfep
, uint32_t command_word
, int nbits
)
200 ASSERT(mutex_owned(dmfep
->milock
));
202 /* Write Preamble & Command & return to tristate */
203 dmfe_poke_mii(dmfep
, MII_PREAMBLE
, 2*mii_reg_size
);
204 dmfe_poke_mii(dmfep
, command_word
, nbits
);
205 dmfe_tristate_mii(dmfep
);
209 dmfe_mii_response(dmfe_t
*dmfep
)
216 /* Check that the PHY generated a zero bit on the 2nd clock */
217 tmp
= dmfe_chip_get32(dmfep
, ETHER_ROM_REG
);
218 ack
= (tmp
& MII_DATA_IN
) == 0;
221 for (data
= 0, i
= 0; i
< mii_reg_size
; ++i
) {
222 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, MII_READ
);
223 drv_usecwait(MII_DELAY
);
224 dmfe_chip_put32(dmfep
, ETHER_ROM_REG
, MII_READ
| MII_CLOCK
);
225 drv_usecwait(MII_DELAY
);
226 tmp
= dmfe_chip_get32(dmfep
, ETHER_ROM_REG
);
228 data
|= (tmp
>> MII_DATA_IN_SHIFT
) & 1;
231 /* leave the interface tristated */
232 dmfe_tristate_mii(dmfep
);
234 return (ack
? data
: ~0);
238 * ======== Next level: 16-bit PHY register access routines ========
242 dmfe_mii_write(void *arg
, uint8_t phy_num
, uint8_t reg_num
, uint16_t reg_dat
)
245 uint32_t command_word
;
247 /* Issue MII command */
248 mutex_enter(dmfep
->milock
);
249 command_word
= MII_WRITE_FRAME
;
250 command_word
|= phy_num
<< MII_PHY_ADDR_SHIFT
;
251 command_word
|= reg_num
<< MII_REG_ADDR_SHIFT
;
252 command_word
|= reg_dat
;
253 dmfe_mii_command(dmfep
, command_word
, 2*mii_reg_size
);
254 mutex_exit(dmfep
->milock
);
258 dmfe_mii_read(void *arg
, uint8_t phy_num
, uint8_t reg_num
)
261 uint32_t command_word
;
264 /* Issue MII command */
265 command_word
= MII_READ_FRAME
;
266 command_word
|= phy_num
<< MII_PHY_ADDR_SHIFT
;
267 command_word
|= reg_num
<< MII_REG_ADDR_SHIFT
;
269 mutex_enter(dmfep
->milock
);
270 dmfe_mii_command(dmfep
, command_word
, mii_reg_size
-2);
272 rv
= dmfe_mii_response(dmfep
);
273 mutex_exit(dmfep
->milock
);
278 dmfe_mii_notify(void *arg
, link_state_t link
)
282 if (link
== LINK_STATE_UP
) {
283 mutex_enter(dmfep
->oplock
);
285 * Configure DUPLEX setting on MAC.
287 if (mii_get_duplex(dmfep
->mii
) == LINK_DUPLEX_FULL
) {
288 dmfep
->opmode
|= FULL_DUPLEX
;
290 dmfep
->opmode
&= ~FULL_DUPLEX
;
292 dmfe_chip_put32(dmfep
, OPN_MODE_REG
, dmfep
->opmode
);
293 mutex_exit(dmfep
->oplock
);
295 mac_link_update(dmfep
->mh
, link
);
300 * PHY initialisation, called only once
303 static mii_ops_t dmfe_mii_ops
= {
308 NULL
, /* mii_reset */
312 dmfe_init_phy(dmfe_t
*dmfep
)
314 dmfep
->mii
= mii_alloc(dmfep
, dmfep
->devinfo
, &dmfe_mii_ops
);
315 if (dmfep
->mii
== NULL
) {