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" /* mv88x201x.c */ 27 28 #include "cphy.h" 29 #include "elmer0.h" 30 31 /* 32 * The 88x2010 Rev C. requires some link status registers * to be read 33 * twice in order to get the right values. Future * revisions will fix 34 * this problem and then this macro * can disappear. 35 */ 36 #define MV88x2010_LINK_STATUS_BUGS 1 37 38 static int led_init(struct cphy *cphy) 39 { 40 /* Setup the LED registers so we can turn on/off. 41 * Writing these bits maps control to another 42 * register. mmd(0x1) addr(0x7) 43 */ 44 (void) mdio_write(cphy, 0x3, 0x8304, 0xdddd); 45 return 0; 46 } 47 48 static int led_link(struct cphy *cphy, u32 do_enable) 49 { 50 u32 led = 0; 51 #define LINK_ENABLE_BIT 0x1 52 53 (void) mdio_read(cphy, 0x1, 0x7, &led); 54 55 if (do_enable & LINK_ENABLE_BIT) { 56 led |= LINK_ENABLE_BIT; 57 (void) mdio_write(cphy, 0x1, 0x7, led); 58 } else { 59 led &= ~LINK_ENABLE_BIT; 60 (void) mdio_write(cphy, 0x1, 0x7, led); 61 } 62 return 0; 63 } 64 65 /* Port Reset */ 66 /* ARGSUSED */ 67 static int mv88x201x_reset(struct cphy *cphy, int wait) 68 { 69 /* This can be done through registers. It is not required since 70 * a full chip reset is used. 71 */ 72 return 0; 73 } 74 75 static int mv88x201x_interrupt_enable(struct cphy *cphy) 76 { 77 /* Enable PHY LASI interrupts. */ 78 (void) mdio_write(cphy, 0x1, 0x9002, 0x1); 79 80 /* Enable Marvell interrupts through Elmer0. */ 81 if (t1_is_asic(cphy->adapter)) { 82 u32 elmer; 83 84 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 85 elmer |= ELMER0_GP_BIT6; 86 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 87 } 88 return 0; 89 } 90 91 static int mv88x201x_interrupt_disable(struct cphy *cphy) 92 { 93 /* Disable PHY LASI interrupts. */ 94 (void) mdio_write(cphy, 0x1, 0x9002, 0x0); 95 96 /* Disable Marvell interrupts through Elmer0. */ 97 if (t1_is_asic(cphy->adapter)) { 98 u32 elmer; 99 100 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 101 elmer &= ~ELMER0_GP_BIT6; 102 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 103 } 104 return 0; 105 } 106 107 static int mv88x201x_interrupt_clear(struct cphy *cphy) 108 { 109 u32 elmer; 110 u32 val; 111 112 #ifdef MV88x2010_LINK_STATUS_BUGS 113 /* Required to read twice before clear takes affect. */ 114 (void) mdio_read(cphy, 0x1, 0x9003, &val); 115 (void) mdio_read(cphy, 0x1, 0x9004, &val); 116 (void) mdio_read(cphy, 0x1, 0x9005, &val); 117 118 /* Read this register after the others above it else 119 * the register doesn't clear correctly. 120 */ 121 (void) mdio_read(cphy, 0x1, 0x1, &val); 122 #endif 123 124 /* Clear link status. */ 125 (void) mdio_read(cphy, 0x1, 0x1, &val); 126 /* Clear PHY LASI interrupts. */ 127 (void) mdio_read(cphy, 0x1, 0x9005, &val); 128 129 #ifdef MV88x2010_LINK_STATUS_BUGS 130 /* Do it again. */ 131 (void) mdio_read(cphy, 0x1, 0x9003, &val); 132 (void) mdio_read(cphy, 0x1, 0x9004, &val); 133 #endif 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_BIT6; 139 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 140 } 141 return 0; 142 } 143 144 static int mv88x201x_interrupt_handler(struct cphy *cphy) 145 { 146 /* Clear interrupts */ 147 (void) mv88x201x_interrupt_clear(cphy); 148 149 /* We have only enabled link change interrupts and so 150 * cphy_cause must be a link change interrupt. 151 */ 152 return cphy_cause_link_change; 153 } 154 155 /* ARGSUSED */ 156 static int mv88x201x_set_loopback(struct cphy *cphy, int on) 157 { 158 return 0; 159 } 160 161 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok, 162 int *speed, int *duplex, int *fc) 163 { 164 u32 val = 0; 165 #define LINK_STATUS_BIT 0x4 166 167 if (link_ok) { 168 /* Read link status. */ 169 (void) mdio_read(cphy, 0x1, 0x1, &val); 170 val &= LINK_STATUS_BIT; 171 *link_ok = (val == LINK_STATUS_BIT); 172 /* Turn on/off Link LED */ 173 (void) led_link(cphy, *link_ok); 174 } 175 if (speed) 176 *speed = SPEED_10000; 177 if (duplex) 178 *duplex = DUPLEX_FULL; 179 if (fc) 180 *fc = PAUSE_RX | PAUSE_TX; 181 return 0; 182 } 183 184 static void mv88x201x_destroy(struct cphy *cphy) 185 { 186 t1_os_free((void *) cphy, sizeof(*cphy)); 187 } 188 189 #ifdef C99_NOT_SUPPORTED 190 static struct cphy_ops mv88x201x_ops = { 191 mv88x201x_destroy, 192 mv88x201x_reset, 193 mv88x201x_interrupt_enable, 194 mv88x201x_interrupt_disable, 195 mv88x201x_interrupt_clear, 196 mv88x201x_interrupt_handler, 197 NULL, 198 NULL, 199 NULL, 200 NULL, 201 mv88x201x_set_loopback, 202 NULL, 203 mv88x201x_get_link_status, 204 }; 205 #else 206 static struct cphy_ops mv88x201x_ops = { 207 .destroy = mv88x201x_destroy, 208 .reset = mv88x201x_reset, 209 .interrupt_enable = mv88x201x_interrupt_enable, 210 .interrupt_disable = mv88x201x_interrupt_disable, 211 .interrupt_clear = mv88x201x_interrupt_clear, 212 .interrupt_handler = mv88x201x_interrupt_handler, 213 .get_link_status = mv88x201x_get_link_status, 214 .set_loopback = mv88x201x_set_loopback, 215 }; 216 #endif 217 218 static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr, 219 struct mdio_ops *mdio_ops) 220 { 221 u32 val; 222 struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy)); 223 224 if (!cphy) 225 return NULL; 226 227 cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops); 228 229 /* Commands the PHY to enable XFP's clock. */ 230 (void) mdio_read(cphy, 0x3, 0x8300, &val); 231 (void) mdio_write(cphy, 0x3, 0x8300, val | 1); 232 233 /* Clear link status. Required because of a bug in the PHY. */ 234 (void) mdio_read(cphy, 0x1, 0x8, &val); 235 (void) mdio_read(cphy, 0x3, 0x8, &val); 236 237 /* Allows for Link,Ack LED turn on/off */ 238 (void) led_init(cphy); 239 return cphy; 240 } 241 242 /* Chip Reset */ 243 static int mv88x201x_phy_reset(adapter_t *adapter) 244 { 245 u32 val; 246 247 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val); 248 val &= ~4; 249 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val); 250 DELAY_MS(100); 251 252 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 253 DELAY_MS(1000); 254 255 /* Now lets enable the Laser. Delay 100us */ 256 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val); 257 val |= 0x8000; 258 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val); 259 DELAY_US(100); 260 return 0; 261 } 262 263 struct gphy t1_mv88x201x_ops = { 264 mv88x201x_phy_create, 265 mv88x201x_phy_reset 266 }; 267