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