linux/drivers/net/phy/vitesse.c
<<
>>
Prefs
   1/*
   2 * Driver for Vitesse PHYs
   3 *
   4 * Author: Kriston Carson
   5 *
   6 * Copyright (c) 2005 Freescale Semiconductor, Inc.
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 *
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/mii.h>
  18#include <linux/ethtool.h>
  19#include <linux/phy.h>
  20
  21/* Vitesse Extended Control Register 1 */
  22#define MII_VSC8244_EXT_CON1           0x17
  23#define MII_VSC8244_EXTCON1_INIT       0x0000
  24#define MII_VSC8244_EXTCON1_TX_SKEW_MASK        0x0c00
  25#define MII_VSC8244_EXTCON1_RX_SKEW_MASK        0x0300
  26#define MII_VSC8244_EXTCON1_TX_SKEW     0x0800
  27#define MII_VSC8244_EXTCON1_RX_SKEW     0x0200
  28
  29/* Vitesse Interrupt Mask Register */
  30#define MII_VSC8244_IMASK               0x19
  31#define MII_VSC8244_IMASK_IEN           0x8000
  32#define MII_VSC8244_IMASK_SPEED         0x4000
  33#define MII_VSC8244_IMASK_LINK          0x2000
  34#define MII_VSC8244_IMASK_DUPLEX        0x1000
  35#define MII_VSC8244_IMASK_MASK          0xf000
  36
  37#define MII_VSC8221_IMASK_MASK          0xa000
  38
  39/* Vitesse Interrupt Status Register */
  40#define MII_VSC8244_ISTAT               0x1a
  41#define MII_VSC8244_ISTAT_STATUS        0x8000
  42#define MII_VSC8244_ISTAT_SPEED         0x4000
  43#define MII_VSC8244_ISTAT_LINK          0x2000
  44#define MII_VSC8244_ISTAT_DUPLEX        0x1000
  45
  46/* Vitesse Auxiliary Control/Status Register */
  47#define MII_VSC8244_AUX_CONSTAT         0x1c
  48#define MII_VSC8244_AUXCONSTAT_INIT     0x0000
  49#define MII_VSC8244_AUXCONSTAT_DUPLEX   0x0020
  50#define MII_VSC8244_AUXCONSTAT_SPEED    0x0018
  51#define MII_VSC8244_AUXCONSTAT_GBIT     0x0010
  52#define MII_VSC8244_AUXCONSTAT_100      0x0008
  53
  54#define MII_VSC8221_AUXCONSTAT_INIT     0x0004 /* need to set this bit? */
  55#define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004
  56
  57#define PHY_ID_VSC8244                  0x000fc6c0
  58#define PHY_ID_VSC8221                  0x000fc550
  59
  60MODULE_DESCRIPTION("Vitesse PHY driver");
  61MODULE_AUTHOR("Kriston Carson");
  62MODULE_LICENSE("GPL");
  63
  64static int vsc824x_config_init(struct phy_device *phydev)
  65{
  66        int extcon;
  67        int err;
  68
  69        err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
  70                        MII_VSC8244_AUXCONSTAT_INIT);
  71        if (err < 0)
  72                return err;
  73
  74        extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
  75
  76        if (extcon < 0)
  77                return err;
  78
  79        extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
  80                        MII_VSC8244_EXTCON1_RX_SKEW_MASK);
  81
  82        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
  83                extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
  84                                MII_VSC8244_EXTCON1_RX_SKEW);
  85
  86        err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
  87
  88        return err;
  89}
  90
  91static int vsc824x_ack_interrupt(struct phy_device *phydev)
  92{
  93        int err = 0;
  94        
  95        /*
  96         * Don't bother to ACK the interrupts if interrupts
  97         * are disabled.  The 824x cannot clear the interrupts
  98         * if they are disabled.
  99         */
 100        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 101                err = phy_read(phydev, MII_VSC8244_ISTAT);
 102
 103        return (err < 0) ? err : 0;
 104}
 105
 106static int vsc82xx_config_intr(struct phy_device *phydev)
 107{
 108        int err;
 109
 110        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 111                err = phy_write(phydev, MII_VSC8244_IMASK,
 112                        phydev->drv->phy_id == PHY_ID_VSC8244 ?
 113                                MII_VSC8244_IMASK_MASK :
 114                                MII_VSC8221_IMASK_MASK);
 115        else {
 116                /*
 117                 * The Vitesse PHY cannot clear the interrupt
 118                 * once it has disabled them, so we clear them first
 119                 */
 120                err = phy_read(phydev, MII_VSC8244_ISTAT);
 121
 122                if (err < 0)
 123                        return err;
 124
 125                err = phy_write(phydev, MII_VSC8244_IMASK, 0);
 126        }
 127
 128        return err;
 129}
 130
 131/* Vitesse 824x */
 132static struct phy_driver vsc8244_driver = {
 133        .phy_id         = PHY_ID_VSC8244,
 134        .name           = "Vitesse VSC8244",
 135        .phy_id_mask    = 0x000fffc0,
 136        .features       = PHY_GBIT_FEATURES,
 137        .flags          = PHY_HAS_INTERRUPT,
 138        .config_init    = &vsc824x_config_init,
 139        .config_aneg    = &genphy_config_aneg,
 140        .read_status    = &genphy_read_status,
 141        .ack_interrupt  = &vsc824x_ack_interrupt,
 142        .config_intr    = &vsc82xx_config_intr,
 143        .driver         = { .owner = THIS_MODULE,},
 144};
 145
 146static int vsc8221_config_init(struct phy_device *phydev)
 147{
 148        int err;
 149
 150        err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
 151                        MII_VSC8221_AUXCONSTAT_INIT);
 152        return err;
 153
 154        /* Perhaps we should set EXT_CON1 based on the interface?
 155           Options are 802.3Z SerDes or SGMII */
 156}
 157
 158/* Vitesse 8221 */
 159static struct phy_driver vsc8221_driver = {
 160        .phy_id         = PHY_ID_VSC8221,
 161        .phy_id_mask    = 0x000ffff0,
 162        .name           = "Vitesse VSC8221",
 163        .features       = PHY_GBIT_FEATURES,
 164        .flags          = PHY_HAS_INTERRUPT,
 165        .config_init    = &vsc8221_config_init,
 166        .config_aneg    = &genphy_config_aneg,
 167        .read_status    = &genphy_read_status,
 168        .ack_interrupt  = &vsc824x_ack_interrupt,
 169        .config_intr    = &vsc82xx_config_intr,
 170        .driver         = { .owner = THIS_MODULE,},
 171};
 172
 173static int __init vsc82xx_init(void)
 174{
 175        int err;
 176
 177        err = phy_driver_register(&vsc8244_driver);
 178        if (err < 0)
 179                return err;
 180        err = phy_driver_register(&vsc8221_driver);
 181        if (err < 0)
 182                phy_driver_unregister(&vsc8244_driver);
 183        return err;
 184}
 185
 186static void __exit vsc82xx_exit(void)
 187{
 188        phy_driver_unregister(&vsc8244_driver);
 189        phy_driver_unregister(&vsc8221_driver);
 190}
 191
 192module_init(vsc82xx_init);
 193module_exit(vsc82xx_exit);
 194