18c2ecf20Sopenharmony_ci/***************************************************************************** 28c2ecf20Sopenharmony_ci * * 38c2ecf20Sopenharmony_ci * File: mv88x201x.c * 48c2ecf20Sopenharmony_ci * $Revision: 1.12 $ * 58c2ecf20Sopenharmony_ci * $Date: 2005/04/15 19:27:14 $ * 68c2ecf20Sopenharmony_ci * Description: * 78c2ecf20Sopenharmony_ci * Marvell PHY (mv88x201x) functionality. * 88c2ecf20Sopenharmony_ci * part of the Chelsio 10Gb Ethernet Driver. * 98c2ecf20Sopenharmony_ci * * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify * 118c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License, version 2, as * 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation. * 138c2ecf20Sopenharmony_ci * * 148c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along * 158c2ecf20Sopenharmony_ci * with this program; if not, see <http://www.gnu.org/licenses/>. * 168c2ecf20Sopenharmony_ci * * 178c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * 188c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * 198c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * 208c2ecf20Sopenharmony_ci * * 218c2ecf20Sopenharmony_ci * http://www.chelsio.com * 228c2ecf20Sopenharmony_ci * * 238c2ecf20Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * 248c2ecf20Sopenharmony_ci * All rights reserved. * 258c2ecf20Sopenharmony_ci * * 268c2ecf20Sopenharmony_ci * Maintainers: maintainers@chelsio.com * 278c2ecf20Sopenharmony_ci * * 288c2ecf20Sopenharmony_ci * Authors: Dimitrios Michailidis <dm@chelsio.com> * 298c2ecf20Sopenharmony_ci * Tina Yang <tainay@chelsio.com> * 308c2ecf20Sopenharmony_ci * Felix Marti <felix@chelsio.com> * 318c2ecf20Sopenharmony_ci * Scott Bardone <sbardone@chelsio.com> * 328c2ecf20Sopenharmony_ci * Kurt Ottaway <kottaway@chelsio.com> * 338c2ecf20Sopenharmony_ci * Frank DiMambro <frank@chelsio.com> * 348c2ecf20Sopenharmony_ci * * 358c2ecf20Sopenharmony_ci * History: * 368c2ecf20Sopenharmony_ci * * 378c2ecf20Sopenharmony_ci ****************************************************************************/ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "cphy.h" 408c2ecf20Sopenharmony_ci#include "elmer0.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * The 88x2010 Rev C. requires some link status registers * to be read 448c2ecf20Sopenharmony_ci * twice in order to get the right values. Future * revisions will fix 458c2ecf20Sopenharmony_ci * this problem and then this macro * can disappear. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define MV88x2010_LINK_STATUS_BUGS 1 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int led_init(struct cphy *cphy) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci /* Setup the LED registers so we can turn on/off. 528c2ecf20Sopenharmony_ci * Writing these bits maps control to another 538c2ecf20Sopenharmony_ci * register. mmd(0x1) addr(0x7) 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd); 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int led_link(struct cphy *cphy, u32 do_enable) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u32 led = 0; 628c2ecf20Sopenharmony_ci#define LINK_ENABLE_BIT 0x1 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (do_enable & LINK_ENABLE_BIT) { 678c2ecf20Sopenharmony_ci led |= LINK_ENABLE_BIT; 688c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 698c2ecf20Sopenharmony_ci } else { 708c2ecf20Sopenharmony_ci led &= ~LINK_ENABLE_BIT; 718c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Port Reset */ 778c2ecf20Sopenharmony_cistatic int mv88x201x_reset(struct cphy *cphy, int wait) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci /* This can be done through registers. It is not required since 808c2ecf20Sopenharmony_ci * a full chip reset is used. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_enable(struct cphy *cphy) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci /* Enable PHY LASI interrupts. */ 888c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 898c2ecf20Sopenharmony_ci MDIO_PMA_LASI_LSALARM); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Enable Marvell interrupts through Elmer0. */ 928c2ecf20Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 938c2ecf20Sopenharmony_ci u32 elmer; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 968c2ecf20Sopenharmony_ci elmer |= ELMER0_GP_BIT6; 978c2ecf20Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_disable(struct cphy *cphy) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci /* Disable PHY LASI interrupts. */ 1058c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Disable Marvell interrupts through Elmer0. */ 1088c2ecf20Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 1098c2ecf20Sopenharmony_ci u32 elmer; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); 1128c2ecf20Sopenharmony_ci elmer &= ~ELMER0_GP_BIT6; 1138c2ecf20Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_clear(struct cphy *cphy) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 elmer; 1218c2ecf20Sopenharmony_ci u32 val; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS 1248c2ecf20Sopenharmony_ci /* Required to read twice before clear takes affect. */ 1258c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 1268c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 1278c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Read this register after the others above it else 1308c2ecf20Sopenharmony_ci * the register doesn't clear correctly. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 1338c2ecf20Sopenharmony_ci#endif 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Clear link status. */ 1368c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 1378c2ecf20Sopenharmony_ci /* Clear PHY LASI interrupts. */ 1388c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS 1418c2ecf20Sopenharmony_ci /* Do it again. */ 1428c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val); 1438c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val); 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Clear Marvell interrupts through Elmer0. */ 1478c2ecf20Sopenharmony_ci if (t1_is_asic(cphy->adapter)) { 1488c2ecf20Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); 1498c2ecf20Sopenharmony_ci elmer |= ELMER0_GP_BIT6; 1508c2ecf20Sopenharmony_ci t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_handler(struct cphy *cphy) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci /* Clear interrupts */ 1588c2ecf20Sopenharmony_ci mv88x201x_interrupt_clear(cphy); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* We have only enabled link change interrupts and so 1618c2ecf20Sopenharmony_ci * cphy_cause must be a link change interrupt. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci return cphy_cause_link_change; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int mv88x201x_set_loopback(struct cphy *cphy, int on) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok, 1728c2ecf20Sopenharmony_ci int *speed, int *duplex, int *fc) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u32 val = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (link_ok) { 1778c2ecf20Sopenharmony_ci /* Read link status. */ 1788c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 1798c2ecf20Sopenharmony_ci val &= MDIO_STAT1_LSTATUS; 1808c2ecf20Sopenharmony_ci *link_ok = (val == MDIO_STAT1_LSTATUS); 1818c2ecf20Sopenharmony_ci /* Turn on/off Link LED */ 1828c2ecf20Sopenharmony_ci led_link(cphy, *link_ok); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci if (speed) 1858c2ecf20Sopenharmony_ci *speed = SPEED_10000; 1868c2ecf20Sopenharmony_ci if (duplex) 1878c2ecf20Sopenharmony_ci *duplex = DUPLEX_FULL; 1888c2ecf20Sopenharmony_ci if (fc) 1898c2ecf20Sopenharmony_ci *fc = PAUSE_RX | PAUSE_TX; 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void mv88x201x_destroy(struct cphy *cphy) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci kfree(cphy); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct cphy_ops mv88x201x_ops = { 1998c2ecf20Sopenharmony_ci .destroy = mv88x201x_destroy, 2008c2ecf20Sopenharmony_ci .reset = mv88x201x_reset, 2018c2ecf20Sopenharmony_ci .interrupt_enable = mv88x201x_interrupt_enable, 2028c2ecf20Sopenharmony_ci .interrupt_disable = mv88x201x_interrupt_disable, 2038c2ecf20Sopenharmony_ci .interrupt_clear = mv88x201x_interrupt_clear, 2048c2ecf20Sopenharmony_ci .interrupt_handler = mv88x201x_interrupt_handler, 2058c2ecf20Sopenharmony_ci .get_link_status = mv88x201x_get_link_status, 2068c2ecf20Sopenharmony_ci .set_loopback = mv88x201x_set_loopback, 2078c2ecf20Sopenharmony_ci .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | 2088c2ecf20Sopenharmony_ci MDIO_DEVS_PHYXS | MDIO_DEVS_WIS), 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr, 2128c2ecf20Sopenharmony_ci const struct mdio_ops *mdio_ops) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci u32 val; 2158c2ecf20Sopenharmony_ci struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!cphy) 2188c2ecf20Sopenharmony_ci return NULL; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Commands the PHY to enable XFP's clock. */ 2238c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val); 2248c2ecf20Sopenharmony_ci cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Clear link status. Required because of a bug in the PHY. */ 2278c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val); 2288c2ecf20Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Allows for Link,Ack LED turn on/off */ 2318c2ecf20Sopenharmony_ci led_init(cphy); 2328c2ecf20Sopenharmony_ci return cphy; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* Chip Reset */ 2368c2ecf20Sopenharmony_cistatic int mv88x201x_phy_reset(adapter_t *adapter) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci u32 val; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 2418c2ecf20Sopenharmony_ci val &= ~4; 2428c2ecf20Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 2438c2ecf20Sopenharmony_ci msleep(100); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 2468c2ecf20Sopenharmony_ci msleep(1000); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Now lets enable the Laser. Delay 100us */ 2498c2ecf20Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 2508c2ecf20Sopenharmony_ci val |= 0x8000; 2518c2ecf20Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 2528c2ecf20Sopenharmony_ci udelay(100); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciconst struct gphy t1_mv88x201x_ops = { 2578c2ecf20Sopenharmony_ci .create = mv88x201x_phy_create, 2588c2ecf20Sopenharmony_ci .reset = mv88x201x_phy_reset 2598c2ecf20Sopenharmony_ci}; 260