Home | History | Annotate | Download | only in dmfe
      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
     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
    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
    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
    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
    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
    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
    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
    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
    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