1*d39a76e7Sxw161283 /* 2*d39a76e7Sxw161283 * CDDL HEADER START 3*d39a76e7Sxw161283 * 4*d39a76e7Sxw161283 * The contents of this file are subject to the terms of the 5*d39a76e7Sxw161283 * Common Development and Distribution License (the "License"). 6*d39a76e7Sxw161283 * You may not use this file except in compliance with the License. 7*d39a76e7Sxw161283 * 8*d39a76e7Sxw161283 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*d39a76e7Sxw161283 * or http://www.opensolaris.org/os/licensing. 10*d39a76e7Sxw161283 * See the License for the specific language governing permissions 11*d39a76e7Sxw161283 * and limitations under the License. 12*d39a76e7Sxw161283 * 13*d39a76e7Sxw161283 * When distributing Covered Code, include this CDDL HEADER in each 14*d39a76e7Sxw161283 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*d39a76e7Sxw161283 * If applicable, add the following below this CDDL HEADER, with the 16*d39a76e7Sxw161283 * fields enclosed by brackets "[]" replaced with your own identifying 17*d39a76e7Sxw161283 * information: Portions Copyright [yyyy] [name of copyright owner] 18*d39a76e7Sxw161283 * 19*d39a76e7Sxw161283 * CDDL HEADER END 20*d39a76e7Sxw161283 */ 21*d39a76e7Sxw161283 22*d39a76e7Sxw161283 /* 23*d39a76e7Sxw161283 * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved. 24*d39a76e7Sxw161283 */ 25*d39a76e7Sxw161283 26*d39a76e7Sxw161283 #pragma ident "%Z%%M% %I% %E% SMI" /* mv88x201x.c */ 27*d39a76e7Sxw161283 28*d39a76e7Sxw161283 #include "cphy.h" 29*d39a76e7Sxw161283 #include "elmer0.h" 30*d39a76e7Sxw161283 31*d39a76e7Sxw161283 /* 32*d39a76e7Sxw161283 * The 88x2010 Rev C. requires some link status registers * to be read 33*d39a76e7Sxw161283 * twice in order to get the right values. Future * revisions will fix 34*d39a76e7Sxw161283 * this problem and then this macro * can disappear. 35*d39a76e7Sxw161283 */ 36*d39a76e7Sxw161283 #define MV88x2010_LINK_STATUS_BUGS 1 37*d39a76e7Sxw161283 38*d39a76e7Sxw161283 static int led_init(struct cphy *cphy) 39*d39a76e7Sxw161283 { 40*d39a76e7Sxw161283 /* Setup the LED registers so we can turn on/off. 41*d39a76e7Sxw161283 * Writing these bits maps control to another 42*d39a76e7Sxw161283 * register. mmd(0x1) addr(0x7) 43*d39a76e7Sxw161283 */ 44*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x3, 0x8304, 0xdddd); 45*d39a76e7Sxw161283 return 0; 46*d39a76e7Sxw161283 } 47*d39a76e7Sxw161283 48*d39a76e7Sxw161283 static int led_link(struct cphy *cphy, u32 do_enable) 49*d39a76e7Sxw161283 { 50*d39a76e7Sxw161283 u32 led = 0; 51*d39a76e7Sxw161283 #define LINK_ENABLE_BIT 0x1 52*d39a76e7Sxw161283 53*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x7, &led); 54*d39a76e7Sxw161283 55*d39a76e7Sxw161283 if (do_enable & LINK_ENABLE_BIT) { 56*d39a76e7Sxw161283 led |= LINK_ENABLE_BIT; 57*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x1, 0x7, led); 58*d39a76e7Sxw161283 } else { 59*d39a76e7Sxw161283 led &= ~LINK_ENABLE_BIT; 60*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x1, 0x7, led); 61*d39a76e7Sxw161283 } 62*d39a76e7Sxw161283 return 0; 63*d39a76e7Sxw161283 } 64*d39a76e7Sxw161283 65*d39a76e7Sxw161283 /* Port Reset */ 66*d39a76e7Sxw161283 /* ARGSUSED */ 67*d39a76e7Sxw161283 static int mv88x201x_reset(struct cphy *cphy, int wait) 68*d39a76e7Sxw161283 { 69*d39a76e7Sxw161283 /* This can be done through registers. It is not required since 70*d39a76e7Sxw161283 * a full chip reset is used. 71*d39a76e7Sxw161283 */ 72*d39a76e7Sxw161283 return 0; 73*d39a76e7Sxw161283 } 74*d39a76e7Sxw161283 75*d39a76e7Sxw161283 static int mv88x201x_interrupt_enable(struct cphy *cphy) 76*d39a76e7Sxw161283 { 77*d39a76e7Sxw161283 /* Enable PHY LASI interrupts. */ 78*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x1, 0x9002, 0x1); 79*d39a76e7Sxw161283 80*d39a76e7Sxw161283 /* Enable Marvell interrupts through Elmer0. */ 81*d39a76e7Sxw161283 if (t1_is_asic(cphy->adapter)) { 82*d39a76e7Sxw161283 u32 elmer; 83*d39a76e7Sxw161283 84*d39a76e7Sxw161283 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 85*d39a76e7Sxw161283 elmer |= ELMER0_GP_BIT6; 86*d39a76e7Sxw161283 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 87*d39a76e7Sxw161283 } 88*d39a76e7Sxw161283 return 0; 89*d39a76e7Sxw161283 } 90*d39a76e7Sxw161283 91*d39a76e7Sxw161283 static int mv88x201x_interrupt_disable(struct cphy *cphy) 92*d39a76e7Sxw161283 { 93*d39a76e7Sxw161283 /* Disable PHY LASI interrupts. */ 94*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x1, 0x9002, 0x0); 95*d39a76e7Sxw161283 96*d39a76e7Sxw161283 /* Disable Marvell interrupts through Elmer0. */ 97*d39a76e7Sxw161283 if (t1_is_asic(cphy->adapter)) { 98*d39a76e7Sxw161283 u32 elmer; 99*d39a76e7Sxw161283 100*d39a76e7Sxw161283 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 101*d39a76e7Sxw161283 elmer &= ~ELMER0_GP_BIT6; 102*d39a76e7Sxw161283 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 103*d39a76e7Sxw161283 } 104*d39a76e7Sxw161283 return 0; 105*d39a76e7Sxw161283 } 106*d39a76e7Sxw161283 107*d39a76e7Sxw161283 static int mv88x201x_interrupt_clear(struct cphy *cphy) 108*d39a76e7Sxw161283 { 109*d39a76e7Sxw161283 u32 elmer; 110*d39a76e7Sxw161283 u32 val; 111*d39a76e7Sxw161283 112*d39a76e7Sxw161283 #ifdef MV88x2010_LINK_STATUS_BUGS 113*d39a76e7Sxw161283 /* Required to read twice before clear takes affect. */ 114*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9003, &val); 115*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9004, &val); 116*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9005, &val); 117*d39a76e7Sxw161283 118*d39a76e7Sxw161283 /* Read this register after the others above it else 119*d39a76e7Sxw161283 * the register doesn't clear correctly. 120*d39a76e7Sxw161283 */ 121*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x1, &val); 122*d39a76e7Sxw161283 #endif 123*d39a76e7Sxw161283 124*d39a76e7Sxw161283 /* Clear link status. */ 125*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x1, &val); 126*d39a76e7Sxw161283 /* Clear PHY LASI interrupts. */ 127*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9005, &val); 128*d39a76e7Sxw161283 129*d39a76e7Sxw161283 #ifdef MV88x2010_LINK_STATUS_BUGS 130*d39a76e7Sxw161283 /* Do it again. */ 131*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9003, &val); 132*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x9004, &val); 133*d39a76e7Sxw161283 #endif 134*d39a76e7Sxw161283 135*d39a76e7Sxw161283 /* Clear Marvell interrupts through Elmer0. */ 136*d39a76e7Sxw161283 if (t1_is_asic(cphy->adapter)) { 137*d39a76e7Sxw161283 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); 138*d39a76e7Sxw161283 elmer |= ELMER0_GP_BIT6; 139*d39a76e7Sxw161283 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 140*d39a76e7Sxw161283 } 141*d39a76e7Sxw161283 return 0; 142*d39a76e7Sxw161283 } 143*d39a76e7Sxw161283 144*d39a76e7Sxw161283 static int mv88x201x_interrupt_handler(struct cphy *cphy) 145*d39a76e7Sxw161283 { 146*d39a76e7Sxw161283 /* Clear interrupts */ 147*d39a76e7Sxw161283 (void) mv88x201x_interrupt_clear(cphy); 148*d39a76e7Sxw161283 149*d39a76e7Sxw161283 /* We have only enabled link change interrupts and so 150*d39a76e7Sxw161283 * cphy_cause must be a link change interrupt. 151*d39a76e7Sxw161283 */ 152*d39a76e7Sxw161283 return cphy_cause_link_change; 153*d39a76e7Sxw161283 } 154*d39a76e7Sxw161283 155*d39a76e7Sxw161283 /* ARGSUSED */ 156*d39a76e7Sxw161283 static int mv88x201x_set_loopback(struct cphy *cphy, int on) 157*d39a76e7Sxw161283 { 158*d39a76e7Sxw161283 return 0; 159*d39a76e7Sxw161283 } 160*d39a76e7Sxw161283 161*d39a76e7Sxw161283 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok, 162*d39a76e7Sxw161283 int *speed, int *duplex, int *fc) 163*d39a76e7Sxw161283 { 164*d39a76e7Sxw161283 u32 val = 0; 165*d39a76e7Sxw161283 #define LINK_STATUS_BIT 0x4 166*d39a76e7Sxw161283 167*d39a76e7Sxw161283 if (link_ok) { 168*d39a76e7Sxw161283 /* Read link status. */ 169*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x1, &val); 170*d39a76e7Sxw161283 val &= LINK_STATUS_BIT; 171*d39a76e7Sxw161283 *link_ok = (val == LINK_STATUS_BIT); 172*d39a76e7Sxw161283 /* Turn on/off Link LED */ 173*d39a76e7Sxw161283 (void) led_link(cphy, *link_ok); 174*d39a76e7Sxw161283 } 175*d39a76e7Sxw161283 if (speed) 176*d39a76e7Sxw161283 *speed = SPEED_10000; 177*d39a76e7Sxw161283 if (duplex) 178*d39a76e7Sxw161283 *duplex = DUPLEX_FULL; 179*d39a76e7Sxw161283 if (fc) 180*d39a76e7Sxw161283 *fc = PAUSE_RX | PAUSE_TX; 181*d39a76e7Sxw161283 return 0; 182*d39a76e7Sxw161283 } 183*d39a76e7Sxw161283 184*d39a76e7Sxw161283 static void mv88x201x_destroy(struct cphy *cphy) 185*d39a76e7Sxw161283 { 186*d39a76e7Sxw161283 t1_os_free((void *) cphy, sizeof(*cphy)); 187*d39a76e7Sxw161283 } 188*d39a76e7Sxw161283 189*d39a76e7Sxw161283 #ifdef C99_NOT_SUPPORTED 190*d39a76e7Sxw161283 static struct cphy_ops mv88x201x_ops = { 191*d39a76e7Sxw161283 mv88x201x_destroy, 192*d39a76e7Sxw161283 mv88x201x_reset, 193*d39a76e7Sxw161283 mv88x201x_interrupt_enable, 194*d39a76e7Sxw161283 mv88x201x_interrupt_disable, 195*d39a76e7Sxw161283 mv88x201x_interrupt_clear, 196*d39a76e7Sxw161283 mv88x201x_interrupt_handler, 197*d39a76e7Sxw161283 NULL, 198*d39a76e7Sxw161283 NULL, 199*d39a76e7Sxw161283 NULL, 200*d39a76e7Sxw161283 NULL, 201*d39a76e7Sxw161283 mv88x201x_set_loopback, 202*d39a76e7Sxw161283 NULL, 203*d39a76e7Sxw161283 mv88x201x_get_link_status, 204*d39a76e7Sxw161283 }; 205*d39a76e7Sxw161283 #else 206*d39a76e7Sxw161283 static struct cphy_ops mv88x201x_ops = { 207*d39a76e7Sxw161283 .destroy = mv88x201x_destroy, 208*d39a76e7Sxw161283 .reset = mv88x201x_reset, 209*d39a76e7Sxw161283 .interrupt_enable = mv88x201x_interrupt_enable, 210*d39a76e7Sxw161283 .interrupt_disable = mv88x201x_interrupt_disable, 211*d39a76e7Sxw161283 .interrupt_clear = mv88x201x_interrupt_clear, 212*d39a76e7Sxw161283 .interrupt_handler = mv88x201x_interrupt_handler, 213*d39a76e7Sxw161283 .get_link_status = mv88x201x_get_link_status, 214*d39a76e7Sxw161283 .set_loopback = mv88x201x_set_loopback, 215*d39a76e7Sxw161283 }; 216*d39a76e7Sxw161283 #endif 217*d39a76e7Sxw161283 218*d39a76e7Sxw161283 static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr, 219*d39a76e7Sxw161283 struct mdio_ops *mdio_ops) 220*d39a76e7Sxw161283 { 221*d39a76e7Sxw161283 u32 val; 222*d39a76e7Sxw161283 struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy)); 223*d39a76e7Sxw161283 224*d39a76e7Sxw161283 if (!cphy) 225*d39a76e7Sxw161283 return NULL; 226*d39a76e7Sxw161283 227*d39a76e7Sxw161283 cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops); 228*d39a76e7Sxw161283 229*d39a76e7Sxw161283 /* Commands the PHY to enable XFP's clock. */ 230*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x3, 0x8300, &val); 231*d39a76e7Sxw161283 (void) mdio_write(cphy, 0x3, 0x8300, val | 1); 232*d39a76e7Sxw161283 233*d39a76e7Sxw161283 /* Clear link status. Required because of a bug in the PHY. */ 234*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x1, 0x8, &val); 235*d39a76e7Sxw161283 (void) mdio_read(cphy, 0x3, 0x8, &val); 236*d39a76e7Sxw161283 237*d39a76e7Sxw161283 /* Allows for Link,Ack LED turn on/off */ 238*d39a76e7Sxw161283 (void) led_init(cphy); 239*d39a76e7Sxw161283 return cphy; 240*d39a76e7Sxw161283 } 241*d39a76e7Sxw161283 242*d39a76e7Sxw161283 /* Chip Reset */ 243*d39a76e7Sxw161283 static int mv88x201x_phy_reset(adapter_t *adapter) 244*d39a76e7Sxw161283 { 245*d39a76e7Sxw161283 u32 val; 246*d39a76e7Sxw161283 247*d39a76e7Sxw161283 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val); 248*d39a76e7Sxw161283 val &= ~4; 249*d39a76e7Sxw161283 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val); 250*d39a76e7Sxw161283 DELAY_MS(100); 251*d39a76e7Sxw161283 252*d39a76e7Sxw161283 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 253*d39a76e7Sxw161283 DELAY_MS(1000); 254*d39a76e7Sxw161283 255*d39a76e7Sxw161283 /* Now lets enable the Laser. Delay 100us */ 256*d39a76e7Sxw161283 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val); 257*d39a76e7Sxw161283 val |= 0x8000; 258*d39a76e7Sxw161283 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val); 259*d39a76e7Sxw161283 DELAY_US(100); 260*d39a76e7Sxw161283 return 0; 261*d39a76e7Sxw161283 } 262*d39a76e7Sxw161283 263*d39a76e7Sxw161283 struct gphy t1_mv88x201x_ops = { 264*d39a76e7Sxw161283 mv88x201x_phy_create, 265*d39a76e7Sxw161283 mv88x201x_phy_reset 266*d39a76e7Sxw161283 }; 267