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 /* 23 * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" /* mv88e1xxx.c */ 27 28 #include "common.h" 29 #include "mv88e1xxx.h" 30 #include "cphy.h" 31 #include "elmer0.h" 32 33 /* MV88E1XXX MDI crossover register values */ 34 #define CROSSOVER_MDI 0 35 #define CROSSOVER_MDIX 1 36 #define CROSSOVER_AUTO 3 37 38 #define INTR_ENABLE_MASK 0x6CA0 39 40 /* 41 * Set the bits given by 'bitval' in PHY register 'reg'. 42 */ 43 static void mdio_set_bit(struct cphy *cphy, int reg, u32 bitval) 44 { 45 u32 val; 46 47 (void) simple_mdio_read(cphy, reg, &val); 48 (void) simple_mdio_write(cphy, reg, val | bitval); 49 } 50 51 /* 52 * Clear the bits given by 'bitval' in PHY register 'reg'. 53 */ 54 static void mdio_clear_bit(struct cphy *cphy, int reg, u32 bitval) 55 { 56 u32 val; 57 58 (void) simple_mdio_read(cphy, reg, &val); 59 (void) simple_mdio_write(cphy, reg, val & ~bitval); 60 } 61 62 /* 63 * NAME: phy_reset 64 * 65 * DESC: Reset the given PHY's port. NOTE: This is not a global 66 * chip reset. 67 * 68 * PARAMS: cphy - Pointer to PHY instance data. 69 * 70 * RETURN: 0 - Successfull reset. 71 * -1 - Timeout. 72 */ 73 /* ARGSUSED */ 74 static int mv88e1xxx_reset(struct cphy *cphy, int wait) 75 { 76 u32 ctl; 77 int time_out = 1000; 78 79 mdio_set_bit(cphy, MII_BMCR, BMCR_RESET); 80 81 do { 82 (void) simple_mdio_read(cphy, MII_BMCR, &ctl); 83 ctl &= BMCR_RESET; 84 if (ctl) 85 DELAY_US(1); 86 } while (ctl && --time_out); 87 88 return ctl ? -1 : 0; 89 } 90 91 static int mv88e1xxx_interrupt_enable(struct cphy *cphy) 92 { 93 /* Enable PHY interrupts. */ 94 (void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 95 INTR_ENABLE_MASK); 96 97 /* Enable Marvell interrupts through Elmer0. */ 98 if (t1_is_asic(cphy->adapter)) { 99 u32 elmer; 100 101 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 102 elmer |= ELMER0_GP_BIT1; 103 if (is_T2(cphy->adapter)) { 104 elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; 105 } 106 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 107 } 108 return 0; 109 } 110 111 static int mv88e1xxx_interrupt_disable(struct cphy *cphy) 112 { 113 /* Disable all phy interrupts. */ 114 (void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 0); 115 116 /* Disable Marvell interrupts through Elmer0. */ 117 if (t1_is_asic(cphy->adapter)) { 118 u32 elmer; 119 120 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 121 elmer &= ~ELMER0_GP_BIT1; 122 if (is_T2(cphy->adapter)) { 123 elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4); 124 } 125 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 126 } 127 return 0; 128 } 129 130 static int mv88e1xxx_interrupt_clear(struct cphy *cphy) 131 { 132 u32 elmer; 133 134 /* Clear PHY interrupts by reading the register. */ 135 (void) simple_mdio_read(cphy, MV88E1XXX_INTERRUPT_STATUS_REGISTER, &elmer); 136 137 /* Clear Marvell interrupts through Elmer0. */ 138 if (t1_is_asic(cphy->adapter)) { 139 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); 140 elmer |= ELMER0_GP_BIT1; 141 if (is_T2(cphy->adapter)) { 142 elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; 143 } 144 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 145 } 146 return 0; 147 } 148 149 /* 150 * Set the PHY speed and duplex. This also disables auto-negotiation, except 151 * for 1Gb/s, where auto-negotiation is mandatory. 152 */ 153 static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex) 154 { 155 u32 ctl; 156 157 (void) simple_mdio_read(phy, MII_BMCR, &ctl); 158 if (speed >= 0) { 159 ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); 160 if (speed == SPEED_100) 161 ctl |= BMCR_SPEED100; 162 else if (speed == SPEED_1000) 163 ctl |= BMCR_SPEED1000; 164 } 165 if (duplex >= 0) { 166 ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE); 167 if (duplex == DUPLEX_FULL) 168 ctl |= BMCR_FULLDPLX; 169 } 170 if (ctl & BMCR_SPEED1000) /* auto-negotiation required for 1Gb/s */ 171 ctl |= BMCR_ANENABLE; 172 (void) simple_mdio_write(phy, MII_BMCR, ctl); 173 return 0; 174 } 175 176 static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover) 177 { 178 u32 data32; 179 180 (void) simple_mdio_read(cphy, MV88E1XXX_SPECIFIC_CNTRL_REGISTER, &data32); 181 data32 &= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE); 182 data32 |= V_PSCR_MDI_XOVER_MODE(crossover); 183 (void) simple_mdio_write(cphy, MV88E1XXX_SPECIFIC_CNTRL_REGISTER, data32); 184 return 0; 185 } 186 187 static int mv88e1xxx_autoneg_enable(struct cphy *cphy) 188 { 189 u32 ctl; 190 191 (void) mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO); 192 193 (void) simple_mdio_read(cphy, MII_BMCR, &ctl); 194 /* restart autoneg for change to take effect */ 195 ctl |= BMCR_ANENABLE | BMCR_ANRESTART; 196 (void) simple_mdio_write(cphy, MII_BMCR, ctl); 197 return 0; 198 } 199 200 static int mv88e1xxx_autoneg_disable(struct cphy *cphy) 201 { 202 u32 ctl; 203 204 /* 205 * Crossover *must* be set to manual in order to disable auto-neg. 206 * The Alaska FAQs document highlights this point. 207 */ 208 (void) mv88e1xxx_crossover_set(cphy, CROSSOVER_MDI); 209 210 /* 211 * Must include autoneg reset when disabling auto-neg. This 212 * is described in the Alaska FAQ document. 213 */ 214 (void) simple_mdio_read(cphy, MII_BMCR, &ctl); 215 ctl &= ~BMCR_ANENABLE; 216 (void) simple_mdio_write(cphy, MII_BMCR, ctl | BMCR_ANRESTART); 217 return 0; 218 } 219 220 static int mv88e1xxx_autoneg_restart(struct cphy *cphy) 221 { 222 mdio_set_bit(cphy, MII_BMCR, BMCR_ANRESTART); 223 return 0; 224 } 225 226 static int mv88e1xxx_advertise(struct cphy *phy, unsigned int advertise_map) 227 { 228 u32 val = 0; 229 230 if (advertise_map & 231 (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { 232 (void) simple_mdio_read(phy, MII_GBCR, &val); 233 val &= ~(GBCR_ADV_1000HALF | GBCR_ADV_1000FULL); 234 if (advertise_map & ADVERTISED_1000baseT_Half) 235 val |= GBCR_ADV_1000HALF; 236 if (advertise_map & ADVERTISED_1000baseT_Full) 237 val |= GBCR_ADV_1000FULL; 238 } 239 (void) simple_mdio_write(phy, MII_GBCR, val); 240 241 val = 1; 242 if (advertise_map & ADVERTISED_10baseT_Half) 243 val |= ADVERTISE_10HALF; 244 if (advertise_map & ADVERTISED_10baseT_Full) 245 val |= ADVERTISE_10FULL; 246 if (advertise_map & ADVERTISED_100baseT_Half) 247 val |= ADVERTISE_100HALF; 248 if (advertise_map & ADVERTISED_100baseT_Full) 249 val |= ADVERTISE_100FULL; 250 if (advertise_map & ADVERTISED_PAUSE) 251 val |= ADVERTISE_PAUSE; 252 if (advertise_map & ADVERTISED_ASYM_PAUSE) 253 val |= ADVERTISE_PAUSE_ASYM; 254 (void) simple_mdio_write(phy, MII_ADVERTISE, val); 255 return 0; 256 } 257 258 static int mv88e1xxx_set_loopback(struct cphy *cphy, int on) 259 { 260 if (on) 261 mdio_set_bit(cphy, MII_BMCR, BMCR_LOOPBACK); 262 else 263 mdio_clear_bit(cphy, MII_BMCR, BMCR_LOOPBACK); 264 return 0; 265 } 266 267 static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok, 268 int *speed, int *duplex, int *fc) 269 { 270 u32 status; 271 int sp = -1, dplx = -1, pause = 0; 272 273 (void) simple_mdio_read(cphy, MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status); 274 if ((status & V_PSSR_STATUS_RESOLVED) != 0) { 275 if (status & V_PSSR_RX_PAUSE) 276 pause |= PAUSE_RX; 277 if (status & V_PSSR_TX_PAUSE) 278 pause |= PAUSE_TX; 279 dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 280 sp = G_PSSR_SPEED(status); 281 if (sp == 0) 282 sp = SPEED_10; 283 else if (sp == 1) 284 sp = SPEED_100; 285 else 286 sp = SPEED_1000; 287 } 288 if (link_ok) 289 *link_ok = (status & V_PSSR_LINK) != 0; 290 if (speed) 291 *speed = sp; 292 if (duplex) 293 *duplex = dplx; 294 if (fc) 295 *fc = pause; 296 return 0; 297 } 298 299 static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable) 300 { 301 u32 val; 302 303 (void) simple_mdio_read(cphy, MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, &val); 304 305 /* 306 * Set the downshift counter to 2 so we try to establish Gb link 307 * twice before downshifting. 308 */ 309 val &= ~(V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT)); 310 311 if (downshift_enable) 312 val |= V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2); 313 (void) simple_mdio_write(cphy, MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, val); 314 return 0; 315 } 316 317 static int mv88e1xxx_interrupt_handler(struct cphy *cphy) 318 { 319 int cphy_cause = 0; 320 u32 status; 321 322 /* 323 * Loop until cause reads zero. Need to handle bouncing interrupts. 324 */ 325 /*CONSTCOND*/ 326 while (1) { 327 u32 cause; 328 329 (void) simple_mdio_read(cphy, MV88E1XXX_INTERRUPT_STATUS_REGISTER, 330 &cause); 331 cause &= INTR_ENABLE_MASK; 332 if (!cause) break; 333 334 if (cause & MV88E1XXX_INTR_LINK_CHNG) { 335 (void) simple_mdio_read(cphy, 336 MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status); 337 338 if (status & MV88E1XXX_INTR_LINK_CHNG) { 339 cphy->state |= PHY_LINK_UP; 340 } else { 341 cphy->state &= ~PHY_LINK_UP; 342 if (cphy->state & PHY_AUTONEG_EN) 343 cphy->state &= ~PHY_AUTONEG_RDY; 344 cphy_cause |= cphy_cause_link_change; 345 } 346 } 347 348 if (cause & MV88E1XXX_INTR_AUTONEG_DONE) 349 cphy->state |= PHY_AUTONEG_RDY; 350 351 if ((cphy->state & (PHY_LINK_UP | PHY_AUTONEG_RDY)) == 352 (PHY_LINK_UP | PHY_AUTONEG_RDY)) 353 cphy_cause |= cphy_cause_link_change; 354 } 355 return cphy_cause; 356 } 357 358 static void mv88e1xxx_destroy(struct cphy *cphy) 359 { 360 t1_os_free((void *)cphy, sizeof(*cphy)); 361 } 362 363 #ifdef C99_NOT_SUPPORTED 364 static struct cphy_ops mv88e1xxx_ops = { 365 mv88e1xxx_destroy, 366 mv88e1xxx_reset, 367 mv88e1xxx_interrupt_enable, 368 mv88e1xxx_interrupt_disable, 369 mv88e1xxx_interrupt_clear, 370 mv88e1xxx_interrupt_handler, 371 mv88e1xxx_autoneg_enable, 372 mv88e1xxx_autoneg_disable, 373 mv88e1xxx_autoneg_restart, 374 mv88e1xxx_advertise, 375 mv88e1xxx_set_loopback, 376 mv88e1xxx_set_speed_duplex, 377 mv88e1xxx_get_link_status, 378 }; 379 #else 380 static struct cphy_ops mv88e1xxx_ops = { 381 .destroy = mv88e1xxx_destroy, 382 .reset = mv88e1xxx_reset, 383 .interrupt_enable = mv88e1xxx_interrupt_enable, 384 .interrupt_disable = mv88e1xxx_interrupt_disable, 385 .interrupt_clear = mv88e1xxx_interrupt_clear, 386 .interrupt_handler = mv88e1xxx_interrupt_handler, 387 .autoneg_enable = mv88e1xxx_autoneg_enable, 388 .autoneg_disable = mv88e1xxx_autoneg_disable, 389 .autoneg_restart = mv88e1xxx_autoneg_restart, 390 .advertise = mv88e1xxx_advertise, 391 .set_loopback = mv88e1xxx_set_loopback, 392 .set_speed_duplex = mv88e1xxx_set_speed_duplex, 393 .get_link_status = mv88e1xxx_get_link_status, 394 }; 395 #endif 396 397 static struct cphy *mv88e1xxx_phy_create(adapter_t *adapter, int phy_addr, 398 struct mdio_ops *mdio_ops) 399 { 400 struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy)); 401 402 if (!cphy) return NULL; 403 404 cphy_init(cphy, adapter, phy_addr, &mv88e1xxx_ops, mdio_ops); 405 406 /* Configure particular PHY's to run in a different mode. */ 407 if ((board_info(adapter)->caps & SUPPORTED_TP) && 408 board_info(adapter)->chip_phy == CHBT_PHY_88E1111) { 409 /* 410 * Configure the PHY transmitter as class A to reduce EMI. 411 */ 412 (void) simple_mdio_write(cphy, MV88E1XXX_EXTENDED_ADDR_REGISTER, 0xB); 413 (void) simple_mdio_write(cphy, MV88E1XXX_EXTENDED_REGISTER, 0x8004); 414 } 415 (void) mv88e1xxx_downshift_set(cphy, 1); /* Enable downshift */ 416 417 /* LED */ 418 if (is_T2(adapter)) { 419 (void) simple_mdio_write(cphy, 420 MV88E1XXX_LED_CONTROL_REGISTER, 0x1); 421 } 422 423 return cphy; 424 } 425 426 /* ARGSUSED */ 427 static int mv88e1xxx_phy_reset(adapter_t* adapter) 428 { 429 return 0; 430 } 431 432 struct gphy t1_mv88e1xxx_ops = { 433 mv88e1xxx_phy_create, 434 mv88e1xxx_phy_reset 435 }; 436