1 /***************************************************************************** 2 * * 3 * File: mv88x201x.c * 4 * $Revision: 1.12 $ * 5 * $Date: 2005/04/15 19:27:14 $ * 6 * Description: * 7 * Marvell PHY (mv88x201x) functionality. * 8 * part of the Chelsio 10Gb Ethernet Driver. * 9 * * 10 * This program is free software; you can redistribute it and/or modify * 11 * it under the terms of the GNU General Public License, version 2, as * 12 * published by the Free Software Foundation. * 13 * * 14 * You should have received a copy of the GNU General Public License along * 15 * with this program; if not, write to the Free Software Foundation, Inc., * 16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 17 * * 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * 19 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * 20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * 21 * * 22 * http://www.chelsio.com * 23 * * 24 * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * 25 * All rights reserved. * 26 * * 27 * Maintainers: maintainers@chelsio.com * 28 * * 29 * Authors: Dimitrios Michailidis <dm@chelsio.com> * 30 * Tina Yang <tainay@chelsio.com> * 31 * Felix Marti <felix@chelsio.com> * 32 * Scott Bardone <sbardone@chelsio.com> * 33 * Kurt Ottaway <kottaway@chelsio.com> * 34 * Frank DiMambro <frank@chelsio.com> * 35 * * 36 * History: * 37 * * 38 ****************************************************************************/ 39 40 #include "cphy.h" 41 #include "elmer0.h" 42 43 /* 44 * The 88x2010 Rev C. requires some link status registers * to be read 45 * twice in order to get the right values. Future * revisions will fix 46 * this problem and then this macro * can disappear. 47 */ 48 #define MV88x2010_LINK_STATUS_BUGS 1 49 50 static int led_init(struct cphy *cphy) 51 { 52 /* Setup the LED registers so we can turn on/off. 53 * Writing these bits maps control to another 54 * register. mmd(0x1) addr(0x7) 55 */ 56 cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd); 57 return 0; 58 } 59 60 static int led_link(struct cphy *cphy, u32 do_enable) 61 { 62 u32 led = 0; 63 #define LINK_ENABLE_BIT 0x1 64 65 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led); 66 67 if (do_enable & LINK_ENABLE_BIT) { 68 led |= LINK_ENABLE_BIT; 69 cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 70 } else { 71 led &= ~LINK_ENABLE_BIT; 72 cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 73 } 74 return 0; 75 } 76 77 /* Port Reset */ 78 static int mv88x201x_reset(struct cphy *cphy, int wait) 79 { 80 /* This can be done through registers. It is not required since 81 * a full chip reset is used. 82 */ 83 return 0; 84 } 85 86 static int mv88x201x_interrupt_enable(struct cphy *cphy) 87 { 88 /* Enable PHY LASI interrupts. */ 89 cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 90 MDIO_PMA_LASI_LSALARM); 91 92 /* Enable Marvell interrupts through Elmer0. */ 93 if (t1_is_asic(cphy->adapter)) { 94 u32 elmer; 95 96 t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 97 elmer |= ELMER0_GP_BIT6; 98 t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 99 } 100 return 0; 101 } 102 103 static int mv88x201x_interrupt_disable(struct cphy *cphy) 104 { 105 /* Disable PHY LASI interrupts. */ 106 cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0); 107 108 /* Disable Marvell interrupts through Elmer0. */ 109 if (t1_is_asic(cphy->adapter)) { 110 u32 elmer; 111 112 t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 113 elmer &= ~ELMER0_GP_BIT6; 114 t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 115 } 116 return 0; 117 } 118 119 static int mv88x201x_interrupt_clear(struct cphy *cphy) 120 { 121 u32 elmer; 122 u32 val; 123 124 #ifdef MV88x2010_LINK_STATUS_BUGS 125 /* Required to read twice before clear takes affect. */ 126 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 127 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 128 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 129 130 /* Read this register after the others above it else 131 * the register doesn't clear correctly. 132 */ 133 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 134 #endif 135 136 /* Clear link status. */ 137 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 138 /* Clear PHY LASI interrupts. */ 139 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 140 141 #ifdef MV88x2010_LINK_STATUS_BUGS 142 /* Do it again. */ 143 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 144 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 145 #endif 146 147 /* Clear Marvell interrupts through Elmer0. */ 148 if (t1_is_asic(cphy->adapter)) { 149 t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); 150 elmer |= ELMER0_GP_BIT6; 151 t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 152 } 153 return 0; 154 } 155 156 static int mv88x201x_interrupt_handler(struct cphy *cphy) 157 { 158 /* Clear interrupts */ 159 mv88x201x_interrupt_clear(cphy); 160 161 /* We have only enabled link change interrupts and so 162 * cphy_cause must be a link change interrupt. 163 */ 164 return cphy_cause_link_change; 165 } 166 167 static int mv88x201x_set_loopback(struct cphy *cphy, int on) 168 { 169 return 0; 170 } 171 172 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok, 173 int *speed, int *duplex, int *fc) 174 { 175 u32 val = 0; 176 177 if (link_ok) { 178 /* Read link status. */ 179 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 180 val &= MDIO_STAT1_LSTATUS; 181 *link_ok = (val == MDIO_STAT1_LSTATUS); 182 /* Turn on/off Link LED */ 183 led_link(cphy, *link_ok); 184 } 185 if (speed) 186 *speed = SPEED_10000; 187 if (duplex) 188 *duplex = DUPLEX_FULL; 189 if (fc) 190 *fc = PAUSE_RX | PAUSE_TX; 191 return 0; 192 } 193 194 static void mv88x201x_destroy(struct cphy *cphy) 195 { 196 kfree(cphy); 197 } 198 199 static struct cphy_ops mv88x201x_ops = { 200 .destroy = mv88x201x_destroy, 201 .reset = mv88x201x_reset, 202 .interrupt_enable = mv88x201x_interrupt_enable, 203 .interrupt_disable = mv88x201x_interrupt_disable, 204 .interrupt_clear = mv88x201x_interrupt_clear, 205 .interrupt_handler = mv88x201x_interrupt_handler, 206 .get_link_status = mv88x201x_get_link_status, 207 .set_loopback = mv88x201x_set_loopback, 208 .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | 209 MDIO_DEVS_PHYXS | MDIO_DEVS_WIS), 210 }; 211 212 static struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr, 213 const struct mdio_ops *mdio_ops) 214 { 215 u32 val; 216 struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); 217 218 if (!cphy) 219 return NULL; 220 221 cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops); 222 223 /* Commands the PHY to enable XFP's clock. */ 224 cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val); 225 cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1); 226 227 /* Clear link status. Required because of a bug in the PHY. */ 228 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val); 229 cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val); 230 231 /* Allows for Link,Ack LED turn on/off */ 232 led_init(cphy); 233 return cphy; 234 } 235 236 /* Chip Reset */ 237 static int mv88x201x_phy_reset(adapter_t *adapter) 238 { 239 u32 val; 240 241 t1_tpi_read(adapter, A_ELMER0_GPO, &val); 242 val &= ~4; 243 t1_tpi_write(adapter, A_ELMER0_GPO, val); 244 msleep(100); 245 246 t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 247 msleep(1000); 248 249 /* Now lets enable the Laser. Delay 100us */ 250 t1_tpi_read(adapter, A_ELMER0_GPO, &val); 251 val |= 0x8000; 252 t1_tpi_write(adapter, A_ELMER0_GPO, val); 253 udelay(100); 254 return 0; 255 } 256 257 const struct gphy t1_mv88x201x_ops = { 258 .create = mv88x201x_phy_create, 259 .reset = mv88x201x_phy_reset 260 }; 261