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