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