xref: /illumos-gate/usr/src/uts/common/io/dmfe/dmfe_mii.c (revision bdb9230a)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "dmfe_impl.h"
27 
28 /*
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.
34  */
35 
36 static const int mii_reg_size = 16;			/* bits		*/
37 
38 /*
39  * ======== Low-level SROM access ========
40  */
41 
42 /*
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.
46  */
47 void
dmfe_read_eeprom(dmfe_t * dmfep,uint16_t raddr,uint8_t * ptr,int cnt)48 dmfe_read_eeprom(dmfe_t *dmfep, uint16_t raddr, uint8_t *ptr, int cnt)
49 {
50 	uint16_t value;
51 	uint16_t bit;
52 
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));
58 
59 	raddr /= 2;	/* make it a word address */
60 
61 	/* loop over multiple words... rom access in 16-bit increments */
62 	while (cnt > 0) {
63 
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);
73 
74 		/* send 3 bit read command */
75 		for (bit = HIGH_CMD_BIT; bit != 0; bit >>= 1) {
76 
77 			value = (bit & EEPROM_READ_CMD) ? DATA_IN : 0;
78 
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);
89 		}
90 
91 		/* send 6 bit address */
92 		for (bit = HIGH_ADDRESS_BIT; bit != 0; bit >>= 1) {
93 			value = (bit & raddr) ? DATA_IN : 0;
94 
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);
105 		}
106 
107 		/* shift out data */
108 		value = 0;
109 		for (bit = HIGH_DATA_BIT; bit != 0; bit >>= 1) {
110 
111 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
112 			    READ_EEPROM_CS | SEL_CLK);
113 			drv_usecwait(1);
114 
115 			if (dmfe_chip_get32(dmfep, ETHER_ROM_REG) & DATA_OUT)
116 				value |= bit;
117 			drv_usecwait(1);
118 
119 			dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
120 			drv_usecwait(1);
121 		}
122 
123 		/* turn off EEPROM access */
124 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
125 		drv_usecwait(1);
126 
127 		/* this makes it endian neutral */
128 		*ptr++ = value & 0xff;
129 		*ptr++ = (value >> 8);
130 
131 		cnt -= 2;
132 		raddr++;
133 	}
134 }
135 
136 /*
137  * ======== Lowest-level bit-twiddling to drive MII interface ========
138  */
139 
140 /*
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.
144  */
145 static void
dmfe_poke_mii(dmfe_t * dmfep,uint32_t mii_data,uint_t nbits)146 dmfe_poke_mii(dmfe_t *dmfep, uint32_t mii_data, uint_t nbits)
147 {
148 	uint32_t dbit;
149 
150 	ASSERT(mutex_owned(dmfep->milock));
151 
152 	for (; nbits > 0; mii_data <<= 1, --nbits) {
153 		/*
154 		 * Extract the MSB of <mii_data> and shift it to the
155 		 * proper bit position in the MII-poking register
156 		 */
157 		dbit = mii_data >> 31;
158 		dbit <<= MII_DATA_OUT_SHIFT;
159 		ASSERT((dbit & ~MII_DATA_OUT) == 0);
160 
161 		/*
162 		 * Drive the bit across the wire ...
163 		 */
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);
170 	}
171 
172 	dmfe_chip_put32(dmfep, ETHER_ROM_REG,
173 	    MII_WRITE | dbit);				/* Clock Low	*/
174 	drv_usecwait(MII_DELAY);
175 }
176 
177 /*
178  * Put the MDIO port in tri-state for the turn around bits
179  * in MII read and at end of MII management sequence.
180  */
181 static void
dmfe_tristate_mii(dmfe_t * dmfep)182 dmfe_tristate_mii(dmfe_t *dmfep)
183 {
184 	ASSERT(mutex_owned(dmfep->milock));
185 
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);
190 }
191 
192 
193 /*
194  * ======== Next level: issue an MII access command/get a response ========
195  */
196 
197 static void
dmfe_mii_command(dmfe_t * dmfep,uint32_t command_word,int nbits)198 dmfe_mii_command(dmfe_t *dmfep, uint32_t command_word, int nbits)
199 {
200 	ASSERT(mutex_owned(dmfep->milock));
201 
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);
206 }
207 
208 static uint16_t
dmfe_mii_response(dmfe_t * dmfep)209 dmfe_mii_response(dmfe_t *dmfep)
210 {
211 	boolean_t ack;
212 	uint16_t data;
213 	uint32_t tmp;
214 	int i;
215 
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;
219 
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;
229 	}
230 
231 	/* leave the interface tristated */
232 	dmfe_tristate_mii(dmfep);
233 
234 	return (ack ? data : ~0);
235 }
236 
237 /*
238  * ======== Next level: 16-bit PHY register access routines ========
239  */
240 
241 static void
dmfe_mii_write(void * arg,uint8_t phy_num,uint8_t reg_num,uint16_t reg_dat)242 dmfe_mii_write(void *arg, uint8_t phy_num, uint8_t reg_num, uint16_t reg_dat)
243 {
244 	dmfe_t *dmfep = arg;
245 	uint32_t command_word;
246 
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);
255 }
256 
257 static uint16_t
dmfe_mii_read(void * arg,uint8_t phy_num,uint8_t reg_num)258 dmfe_mii_read(void *arg, uint8_t phy_num, uint8_t reg_num)
259 {
260 	dmfe_t *dmfep = arg;
261 	uint32_t command_word;
262 	uint16_t rv;
263 
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;
268 
269 	mutex_enter(dmfep->milock);
270 	dmfe_mii_command(dmfep, command_word, mii_reg_size-2);
271 
272 	rv = dmfe_mii_response(dmfep);
273 	mutex_exit(dmfep->milock);
274 	return (rv);
275 }
276 
277 static void
dmfe_mii_notify(void * arg,link_state_t link)278 dmfe_mii_notify(void *arg, link_state_t link)
279 {
280 	dmfe_t *dmfep = arg;
281 
282 	if (link == LINK_STATE_UP) {
283 		mutex_enter(dmfep->oplock);
284 		/*
285 		 * Configure DUPLEX setting on MAC.
286 		 */
287 		if (mii_get_duplex(dmfep->mii) == LINK_DUPLEX_FULL) {
288 			dmfep->opmode |= FULL_DUPLEX;
289 		} else {
290 			dmfep->opmode &= ~FULL_DUPLEX;
291 		}
292 		dmfe_chip_put32(dmfep, OPN_MODE_REG, dmfep->opmode);
293 		mutex_exit(dmfep->oplock);
294 	}
295 	mac_link_update(dmfep->mh, link);
296 }
297 
298 
299 /*
300  * PHY initialisation, called only once
301  */
302 
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 */
309 };
310 
311 boolean_t
dmfe_init_phy(dmfe_t * dmfep)312 dmfe_init_phy(dmfe_t *dmfep)
313 {
314 	dmfep->mii = mii_alloc(dmfep, dmfep->devinfo, &dmfe_mii_ops);
315 	if (dmfep->mii == NULL) {
316 		return (B_FALSE);
317 	}
318 	return (B_TRUE);
319 }
320