162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/***************************************************************************** 362306a36Sopenharmony_ci * * 462306a36Sopenharmony_ci * File: mv88x201x.c * 562306a36Sopenharmony_ci * $Revision: 1.12 $ * 662306a36Sopenharmony_ci * $Date: 2005/04/15 19:27:14 $ * 762306a36Sopenharmony_ci * Description: * 862306a36Sopenharmony_ci * Marvell PHY (mv88x201x) functionality. * 962306a36Sopenharmony_ci * part of the Chelsio 10Gb Ethernet Driver. * 1062306a36Sopenharmony_ci * * 1162306a36Sopenharmony_ci * * 1262306a36Sopenharmony_ci * http://www.chelsio.com * 1362306a36Sopenharmony_ci * * 1462306a36Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * 1562306a36Sopenharmony_ci * All rights reserved. * 1662306a36Sopenharmony_ci * * 1762306a36Sopenharmony_ci * Maintainers: maintainers@chelsio.com * 1862306a36Sopenharmony_ci * * 1962306a36Sopenharmony_ci * Authors: Dimitrios Michailidis <dm@chelsio.com> * 2062306a36Sopenharmony_ci * Tina Yang <tainay@chelsio.com> * 2162306a36Sopenharmony_ci * Felix Marti <felix@chelsio.com> * 2262306a36Sopenharmony_ci * Scott Bardone <sbardone@chelsio.com> * 2362306a36Sopenharmony_ci * Kurt Ottaway <kottaway@chelsio.com> * 2462306a36Sopenharmony_ci * Frank DiMambro <frank@chelsio.com> * 2562306a36Sopenharmony_ci * * 2662306a36Sopenharmony_ci * History: * 2762306a36Sopenharmony_ci * * 2862306a36Sopenharmony_ci ****************************************************************************/ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "cphy.h" 3162306a36Sopenharmony_ci#include "elmer0.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * The 88x2010 Rev C. requires some link status registers * to be read 3562306a36Sopenharmony_ci * twice in order to get the right values. Future * revisions will fix 3662306a36Sopenharmony_ci * this problem and then this macro * can disappear. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#define MV88x2010_LINK_STATUS_BUGS 1 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int led_init(struct cphy *cphy) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci /* Setup the LED registers so we can turn on/off. 4362306a36Sopenharmony_ci * Writing these bits maps control to another 4462306a36Sopenharmony_ci * register. mmd(0x1) addr(0x7) 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd); 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int led_link(struct cphy *cphy, u32 do_enable) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 led = 0; 5362306a36Sopenharmony_ci#define LINK_ENABLE_BIT 0x1 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (do_enable & LINK_ENABLE_BIT) { 5862306a36Sopenharmony_ci led |= LINK_ENABLE_BIT; 5962306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 6062306a36Sopenharmony_ci } else { 6162306a36Sopenharmony_ci led &= ~LINK_ENABLE_BIT; 6262306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Port Reset */ 6862306a36Sopenharmony_cistatic int mv88x201x_reset(struct cphy *cphy, int wait) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci /* This can be done through registers. It is not required since 7162306a36Sopenharmony_ci * a full chip reset is used. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int mv88x201x_interrupt_enable(struct cphy *cphy) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci /* Enable PHY LASI interrupts. */ 7962306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 8062306a36Sopenharmony_ci MDIO_PMA_LASI_LSALARM); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Enable Marvell interrupts through Elmer0. */ 8362306a36Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 8462306a36Sopenharmony_ci u32 elmer; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 8762306a36Sopenharmony_ci elmer |= ELMER0_GP_BIT6; 8862306a36Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int mv88x201x_interrupt_disable(struct cphy *cphy) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci /* Disable PHY LASI interrupts. */ 9662306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Disable Marvell interrupts through Elmer0. */ 9962306a36Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 10062306a36Sopenharmony_ci u32 elmer; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 10362306a36Sopenharmony_ci elmer &= ~ELMER0_GP_BIT6; 10462306a36Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int mv88x201x_interrupt_clear(struct cphy *cphy) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci u32 elmer; 11262306a36Sopenharmony_ci u32 val; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS 11562306a36Sopenharmony_ci /* Required to read twice before clear takes affect. */ 11662306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 11762306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 11862306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Read this register after the others above it else 12162306a36Sopenharmony_ci * the register doesn't clear correctly. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 12462306a36Sopenharmony_ci#endif 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Clear link status. */ 12762306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 12862306a36Sopenharmony_ci /* Clear PHY LASI interrupts. */ 12962306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS 13262306a36Sopenharmony_ci /* Do it again. */ 13362306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 13462306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 13562306a36Sopenharmony_ci#endif 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Clear Marvell interrupts through Elmer0. */ 13862306a36Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 13962306a36Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); 14062306a36Sopenharmony_ci elmer |= ELMER0_GP_BIT6; 14162306a36Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int mv88x201x_interrupt_handler(struct cphy *cphy) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci /* Clear interrupts */ 14962306a36Sopenharmony_ci mv88x201x_interrupt_clear(cphy); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* We have only enabled link change interrupts and so 15262306a36Sopenharmony_ci * cphy_cause must be a link change interrupt. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci return cphy_cause_link_change; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int mv88x201x_set_loopback(struct cphy *cphy, int on) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok, 16362306a36Sopenharmony_ci int *speed, int *duplex, int *fc) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u32 val = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (link_ok) { 16862306a36Sopenharmony_ci /* Read link status. */ 16962306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 17062306a36Sopenharmony_ci val &= MDIO_STAT1_LSTATUS; 17162306a36Sopenharmony_ci *link_ok = (val == MDIO_STAT1_LSTATUS); 17262306a36Sopenharmony_ci /* Turn on/off Link LED */ 17362306a36Sopenharmony_ci led_link(cphy, *link_ok); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci if (speed) 17662306a36Sopenharmony_ci *speed = SPEED_10000; 17762306a36Sopenharmony_ci if (duplex) 17862306a36Sopenharmony_ci *duplex = DUPLEX_FULL; 17962306a36Sopenharmony_ci if (fc) 18062306a36Sopenharmony_ci *fc = PAUSE_RX | PAUSE_TX; 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void mv88x201x_destroy(struct cphy *cphy) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci kfree(cphy); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct cphy_ops mv88x201x_ops = { 19062306a36Sopenharmony_ci .destroy = mv88x201x_destroy, 19162306a36Sopenharmony_ci .reset = mv88x201x_reset, 19262306a36Sopenharmony_ci .interrupt_enable = mv88x201x_interrupt_enable, 19362306a36Sopenharmony_ci .interrupt_disable = mv88x201x_interrupt_disable, 19462306a36Sopenharmony_ci .interrupt_clear = mv88x201x_interrupt_clear, 19562306a36Sopenharmony_ci .interrupt_handler = mv88x201x_interrupt_handler, 19662306a36Sopenharmony_ci .get_link_status = mv88x201x_get_link_status, 19762306a36Sopenharmony_ci .set_loopback = mv88x201x_set_loopback, 19862306a36Sopenharmony_ci .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | 19962306a36Sopenharmony_ci MDIO_DEVS_PHYXS | MDIO_DEVS_WIS), 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr, 20362306a36Sopenharmony_ci const struct mdio_ops *mdio_ops) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 val; 20662306a36Sopenharmony_ci struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!cphy) 20962306a36Sopenharmony_ci return NULL; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Commands the PHY to enable XFP's clock. */ 21462306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val); 21562306a36Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Clear link status. Required because of a bug in the PHY. */ 21862306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val); 21962306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Allows for Link,Ack LED turn on/off */ 22262306a36Sopenharmony_ci led_init(cphy); 22362306a36Sopenharmony_ci return cphy; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* Chip Reset */ 22762306a36Sopenharmony_cistatic int mv88x201x_phy_reset(adapter_t *adapter) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci u32 val; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 23262306a36Sopenharmony_ci val &= ~4; 23362306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 23462306a36Sopenharmony_ci msleep(100); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 23762306a36Sopenharmony_ci msleep(1000); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Now lets enable the Laser. Delay 100us */ 24062306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 24162306a36Sopenharmony_ci val |= 0x8000; 24262306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 24362306a36Sopenharmony_ci udelay(100); 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciconst struct gphy t1_mv88x201x_ops = { 24862306a36Sopenharmony_ci .create = mv88x201x_phy_create, 24962306a36Sopenharmony_ci .reset = mv88x201x_phy_reset 25062306a36Sopenharmony_ci}; 251