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