Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*****************************************************************************
0003  *                                                                           *
0004  * File: mv88x201x.c                                                         *
0005  * $Revision: 1.12 $                                                         *
0006  * $Date: 2005/04/15 19:27:14 $                                              *
0007  * Description:                                                              *
0008  *  Marvell PHY (mv88x201x) functionality.                                   *
0009  *  part of the Chelsio 10Gb Ethernet Driver.                                *
0010  *                                                                           *
0011  *                                                                           *
0012  * http://www.chelsio.com                                                    *
0013  *                                                                           *
0014  * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
0015  * All rights reserved.                                                      *
0016  *                                                                           *
0017  * Maintainers: maintainers@chelsio.com                                      *
0018  *                                                                           *
0019  * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
0020  *          Tina Yang               <tainay@chelsio.com>                     *
0021  *          Felix Marti             <felix@chelsio.com>                      *
0022  *          Scott Bardone           <sbardone@chelsio.com>                   *
0023  *          Kurt Ottaway            <kottaway@chelsio.com>                   *
0024  *          Frank DiMambro          <frank@chelsio.com>                      *
0025  *                                                                           *
0026  * History:                                                                  *
0027  *                                                                           *
0028  ****************************************************************************/
0029 
0030 #include "cphy.h"
0031 #include "elmer0.h"
0032 
0033 /*
0034  * The 88x2010 Rev C. requires some link status registers * to be read
0035  * twice in order to get the right values. Future * revisions will fix
0036  * this problem and then this macro * can disappear.
0037  */
0038 #define MV88x2010_LINK_STATUS_BUGS    1
0039 
0040 static int led_init(struct cphy *cphy)
0041 {
0042     /* Setup the LED registers so we can turn on/off.
0043      * Writing these bits maps control to another
0044      * register. mmd(0x1) addr(0x7)
0045      */
0046     cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);
0047     return 0;
0048 }
0049 
0050 static int led_link(struct cphy *cphy, u32 do_enable)
0051 {
0052     u32 led = 0;
0053 #define LINK_ENABLE_BIT 0x1
0054 
0055     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led);
0056 
0057     if (do_enable & LINK_ENABLE_BIT) {
0058         led |= LINK_ENABLE_BIT;
0059         cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
0060     } else {
0061         led &= ~LINK_ENABLE_BIT;
0062         cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
0063     }
0064     return 0;
0065 }
0066 
0067 /* Port Reset */
0068 static int mv88x201x_reset(struct cphy *cphy, int wait)
0069 {
0070     /* This can be done through registers.  It is not required since
0071      * a full chip reset is used.
0072      */
0073     return 0;
0074 }
0075 
0076 static int mv88x201x_interrupt_enable(struct cphy *cphy)
0077 {
0078     /* Enable PHY LASI interrupts. */
0079     cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
0080             MDIO_PMA_LASI_LSALARM);
0081 
0082     /* Enable Marvell interrupts through Elmer0. */
0083     if (t1_is_asic(cphy->adapter)) {
0084         u32 elmer;
0085 
0086         t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
0087         elmer |= ELMER0_GP_BIT6;
0088         t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
0089     }
0090     return 0;
0091 }
0092 
0093 static int mv88x201x_interrupt_disable(struct cphy *cphy)
0094 {
0095     /* Disable PHY LASI interrupts. */
0096     cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0);
0097 
0098     /* Disable Marvell interrupts through Elmer0. */
0099     if (t1_is_asic(cphy->adapter)) {
0100         u32 elmer;
0101 
0102         t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
0103         elmer &= ~ELMER0_GP_BIT6;
0104         t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
0105     }
0106     return 0;
0107 }
0108 
0109 static int mv88x201x_interrupt_clear(struct cphy *cphy)
0110 {
0111     u32 elmer;
0112     u32 val;
0113 
0114 #ifdef MV88x2010_LINK_STATUS_BUGS
0115     /* Required to read twice before clear takes affect. */
0116     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
0117     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
0118     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
0119 
0120     /* Read this register after the others above it else
0121      * the register doesn't clear correctly.
0122      */
0123     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
0124 #endif
0125 
0126     /* Clear link status. */
0127     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
0128     /* Clear PHY LASI interrupts. */
0129     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
0130 
0131 #ifdef MV88x2010_LINK_STATUS_BUGS
0132     /* Do it again. */
0133     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
0134     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
0135 #endif
0136 
0137     /* Clear Marvell interrupts through Elmer0. */
0138     if (t1_is_asic(cphy->adapter)) {
0139         t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
0140         elmer |= ELMER0_GP_BIT6;
0141         t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
0142     }
0143     return 0;
0144 }
0145 
0146 static int mv88x201x_interrupt_handler(struct cphy *cphy)
0147 {
0148     /* Clear interrupts */
0149     mv88x201x_interrupt_clear(cphy);
0150 
0151     /* We have only enabled link change interrupts and so
0152      * cphy_cause must be a link change interrupt.
0153      */
0154     return cphy_cause_link_change;
0155 }
0156 
0157 static int mv88x201x_set_loopback(struct cphy *cphy, int on)
0158 {
0159     return 0;
0160 }
0161 
0162 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
0163                      int *speed, int *duplex, int *fc)
0164 {
0165     u32 val = 0;
0166 
0167     if (link_ok) {
0168         /* Read link status. */
0169         cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
0170         val &= MDIO_STAT1_LSTATUS;
0171         *link_ok = (val == MDIO_STAT1_LSTATUS);
0172         /* Turn on/off Link LED */
0173         led_link(cphy, *link_ok);
0174     }
0175     if (speed)
0176         *speed = SPEED_10000;
0177     if (duplex)
0178         *duplex = DUPLEX_FULL;
0179     if (fc)
0180         *fc = PAUSE_RX | PAUSE_TX;
0181     return 0;
0182 }
0183 
0184 static void mv88x201x_destroy(struct cphy *cphy)
0185 {
0186     kfree(cphy);
0187 }
0188 
0189 static const struct cphy_ops mv88x201x_ops = {
0190     .destroy           = mv88x201x_destroy,
0191     .reset             = mv88x201x_reset,
0192     .interrupt_enable  = mv88x201x_interrupt_enable,
0193     .interrupt_disable = mv88x201x_interrupt_disable,
0194     .interrupt_clear   = mv88x201x_interrupt_clear,
0195     .interrupt_handler = mv88x201x_interrupt_handler,
0196     .get_link_status   = mv88x201x_get_link_status,
0197     .set_loopback      = mv88x201x_set_loopback,
0198     .mmds              = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
0199                   MDIO_DEVS_PHYXS | MDIO_DEVS_WIS),
0200 };
0201 
0202 static struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr,
0203                      const struct mdio_ops *mdio_ops)
0204 {
0205     u32 val;
0206     struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
0207 
0208     if (!cphy)
0209         return NULL;
0210 
0211     cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops);
0212 
0213     /* Commands the PHY to enable XFP's clock. */
0214     cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val);
0215     cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1);
0216 
0217     /* Clear link status. Required because of a bug in the PHY.  */
0218     cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val);
0219     cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val);
0220 
0221     /* Allows for Link,Ack LED turn on/off */
0222     led_init(cphy);
0223     return cphy;
0224 }
0225 
0226 /* Chip Reset */
0227 static int mv88x201x_phy_reset(adapter_t *adapter)
0228 {
0229     u32 val;
0230 
0231     t1_tpi_read(adapter, A_ELMER0_GPO, &val);
0232     val &= ~4;
0233     t1_tpi_write(adapter, A_ELMER0_GPO, val);
0234     msleep(100);
0235 
0236     t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
0237     msleep(1000);
0238 
0239     /* Now lets enable the Laser. Delay 100us */
0240     t1_tpi_read(adapter, A_ELMER0_GPO, &val);
0241     val |= 0x8000;
0242     t1_tpi_write(adapter, A_ELMER0_GPO, val);
0243     udelay(100);
0244     return 0;
0245 }
0246 
0247 const struct gphy t1_mv88x201x_ops = {
0248     .create = mv88x201x_phy_create,
0249     .reset = mv88x201x_phy_reset
0250 };