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