Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / dmfe / dmfe_mii.c
blobaeeffec5ec28809bcd39b7c1b2a0186b14c25a25
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
47 void
48 dmfe_read_eeprom(dmfe_t *dmfep, uint16_t raddr, uint8_t *ptr, int cnt)
50 uint16_t value;
51 uint16_t bit;
53 /* only a whole number of words for now */
54 ASSERT((cnt % 2) == 0);
55 ASSERT((raddr % 2) == 0);
56 ASSERT(cnt > 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 */
62 while (cnt > 0) {
64 /* select the eeprom */
65 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
66 drv_usecwait(1);
67 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
68 drv_usecwait(1);
69 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS | SEL_CLK);
70 drv_usecwait(1);
71 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
72 drv_usecwait(1);
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);
82 drv_usecwait(1);
83 dmfe_chip_put32(dmfep, ETHER_ROM_REG,
84 READ_EEPROM_CS | SEL_CLK | value);
85 drv_usecwait(1);
86 dmfe_chip_put32(dmfep, ETHER_ROM_REG,
87 READ_EEPROM_CS | value);
88 drv_usecwait(1);
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);
98 drv_usecwait(1);
99 dmfe_chip_put32(dmfep, ETHER_ROM_REG,
100 READ_EEPROM_CS | SEL_CLK | value);
101 drv_usecwait(1);
102 dmfe_chip_put32(dmfep, ETHER_ROM_REG,
103 READ_EEPROM_CS | value);
104 drv_usecwait(1);
107 /* shift out data */
108 value = 0;
109 for (bit = HIGH_DATA_BIT; bit != 0; bit >>= 1) {
111 dmfe_chip_put32(dmfep, ETHER_ROM_REG,
112 READ_EEPROM_CS | SEL_CLK);
113 drv_usecwait(1);
115 if (dmfe_chip_get32(dmfep, ETHER_ROM_REG) & DATA_OUT)
116 value |= bit;
117 drv_usecwait(1);
119 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
120 drv_usecwait(1);
123 /* turn off EEPROM access */
124 dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
125 drv_usecwait(1);
127 /* this makes it endian neutral */
128 *ptr++ = value & 0xff;
129 *ptr++ = (value >> 8);
131 cnt -= 2;
132 raddr++;
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.
145 static void
146 dmfe_poke_mii(dmfe_t *dmfep, uint32_t mii_data, uint_t nbits)
148 uint32_t dbit;
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.
181 static void
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 ========
197 static void
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);
208 static uint16_t
209 dmfe_mii_response(dmfe_t *dmfep)
211 boolean_t ack;
212 uint16_t data;
213 uint32_t tmp;
214 int i;
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;
220 /* read data WORD */
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);
227 data <<= 1;
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 ========
241 static void
242 dmfe_mii_write(void *arg, uint8_t phy_num, uint8_t reg_num, uint16_t reg_dat)
244 dmfe_t *dmfep = arg;
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);
257 static uint16_t
258 dmfe_mii_read(void *arg, uint8_t phy_num, uint8_t reg_num)
260 dmfe_t *dmfep = arg;
261 uint32_t command_word;
262 uint16_t rv;
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);
274 return (rv);
277 static void
278 dmfe_mii_notify(void *arg, link_state_t link)
280 dmfe_t *dmfep = arg;
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;
289 } else {
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 = {
304 MII_OPS_VERSION,
305 dmfe_mii_read,
306 dmfe_mii_write,
307 dmfe_mii_notify,
308 NULL, /* mii_reset */
311 boolean_t
312 dmfe_init_phy(dmfe_t *dmfep)
314 dmfep->mii = mii_alloc(dmfep, dmfep->devinfo, &dmfe_mii_ops);
315 if (dmfep->mii == NULL) {
316 return (B_FALSE);
318 return (B_TRUE);