251 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*****************************************************************************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 read35 * twice in order to get the right values. Future * revisions will fix36 * this problem and then this macro * can disappear.37 */38#define MV88x2010_LINK_STATUS_BUGS 139 40static 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 another44 * register. mmd(0x1) addr(0x7)45 */46 cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);47 return 0;48}49 50static int led_link(struct cphy *cphy, u32 do_enable)51{52 u32 led = 0;53#define LINK_ENABLE_BIT 0x154 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 */68static int mv88x201x_reset(struct cphy *cphy, int wait)69{70 /* This can be done through registers. It is not required since71 * a full chip reset is used.72 */73 return 0;74}75 76static 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 93static 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 109static int mv88x201x_interrupt_clear(struct cphy *cphy)110{111 u32 elmer;112 u32 val;113 114#ifdef MV88x2010_LINK_STATUS_BUGS115 /* 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 else121 * the register doesn't clear correctly.122 */123 cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);124#endif125 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_BUGS132 /* 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#endif136 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 146static 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 so152 * cphy_cause must be a link change interrupt.153 */154 return cphy_cause_link_change;155}156 157static int mv88x201x_set_loopback(struct cphy *cphy, int on)158{159 return 0;160}161 162static 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 184static void mv88x201x_destroy(struct cphy *cphy)185{186 kfree(cphy);187}188 189static 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 202static 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 */227static 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 247const struct gphy t1_mv88x201x_ops = {248 .create = mv88x201x_phy_create,249 .reset = mv88x201x_phy_reset250};251