linux/drivers/net/phy/national.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/national.c
   3 *
   4 * Driver for National Semiconductor PHYs
   5 *
   6 * Author: Stuart Menefy <stuart.menefy@st.com>
   7 * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
   8 *
   9 * Copyright (c) 2008 STMicroelectronics Limited
  10 *
  11 * This program is free software; you can redistribute  it and/or modify it
  12 * under  the terms of  the GNU General  Public License as published by the
  13 * Free Software Foundation;  either version 2 of the  License, or (at your
  14 * option) any later version.
  15 *
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/mii.h>
  21#include <linux/ethtool.h>
  22#include <linux/phy.h>
  23#include <linux/netdevice.h>
  24
  25/* DP83865 phy identifier values */
  26#define DP83865_PHY_ID  0x20005c7a
  27
  28#define DP83865_INT_MASK_REG 0x15
  29#define DP83865_INT_MASK_STATUS 0x14
  30
  31#define DP83865_INT_REMOTE_FAULT 0x0008
  32#define DP83865_INT_ANE_COMPLETED 0x0010
  33#define DP83865_INT_LINK_CHANGE 0xe000
  34#define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \
  35                                DP83865_INT_ANE_COMPLETED | \
  36                                DP83865_INT_LINK_CHANGE)
  37
  38/* Advanced proprietary configuration */
  39#define NS_EXP_MEM_CTL  0x16
  40#define NS_EXP_MEM_DATA 0x1d
  41#define NS_EXP_MEM_ADD  0x1e
  42
  43#define LED_CTRL_REG 0x13
  44#define AN_FALLBACK_AN 0x0001
  45#define AN_FALLBACK_CRC 0x0002
  46#define AN_FALLBACK_IE 0x0004
  47#define ALL_FALLBACK_ON (AN_FALLBACK_AN |  AN_FALLBACK_CRC | AN_FALLBACK_IE)
  48
  49enum hdx_loopback {
  50        hdx_loopback_on = 0,
  51        hdx_loopback_off = 1,
  52};
  53
  54static u8 ns_exp_read(struct phy_device *phydev, u16 reg)
  55{
  56        phy_write(phydev, NS_EXP_MEM_ADD, reg);
  57        return phy_read(phydev, NS_EXP_MEM_DATA);
  58}
  59
  60static void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data)
  61{
  62        phy_write(phydev, NS_EXP_MEM_ADD, reg);
  63        phy_write(phydev, NS_EXP_MEM_DATA, data);
  64}
  65
  66static int ns_config_intr(struct phy_device *phydev)
  67{
  68        int err;
  69
  70        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  71                err = phy_write(phydev, DP83865_INT_MASK_REG,
  72                                DP83865_INT_MASK_DEFAULT);
  73        else
  74                err = phy_write(phydev, DP83865_INT_MASK_REG, 0);
  75
  76        return err;
  77}
  78
  79static int ns_ack_interrupt(struct phy_device *phydev)
  80{
  81        int ret = phy_read(phydev, DP83865_INT_MASK_STATUS);
  82        if (ret < 0)
  83                return ret;
  84
  85        return 0;
  86}
  87
  88static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)
  89{
  90        int bmcr = phy_read(phydev, MII_BMCR);
  91
  92        phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN));
  93
  94        /* Enable 8 bit expended memory read/write (no auto increment) */
  95        phy_write(phydev, NS_EXP_MEM_CTL, 0);
  96        phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0);
  97        phy_write(phydev, NS_EXP_MEM_DATA, 0x0008);
  98        phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN));
  99        phy_write(phydev, LED_CTRL_REG, mode);
 100}
 101
 102static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable)
 103{
 104        if (disable)
 105                ns_exp_write(phydev, 0x1c0, ns_exp_read(phydev, 0x1c0) | 1);
 106        else
 107                ns_exp_write(phydev, 0x1c0,
 108                             ns_exp_read(phydev, 0x1c0) & 0xfffe);
 109
 110        printk(KERN_DEBUG "DP83865 PHY: 10BASE-T HDX loopback %s\n",
 111               (ns_exp_read(phydev, 0x1c0) & 0x0001) ? "off" : "on");
 112}
 113
 114static int ns_config_init(struct phy_device *phydev)
 115{
 116        ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON);
 117        /* In the latest MAC or switches design, the 10 Mbps loopback
 118           is desired to be turned off. */
 119        ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off);
 120        return ns_ack_interrupt(phydev);
 121}
 122
 123static struct phy_driver dp83865_driver = {
 124        .phy_id = DP83865_PHY_ID,
 125        .phy_id_mask = 0xfffffff0,
 126        .name = "NatSemi DP83865",
 127        .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause,
 128        .flags = PHY_HAS_INTERRUPT,
 129        .config_init = ns_config_init,
 130        .config_aneg = genphy_config_aneg,
 131        .read_status = genphy_read_status,
 132        .ack_interrupt = ns_ack_interrupt,
 133        .config_intr = ns_config_intr,
 134        .driver = {.owner = THIS_MODULE,}
 135};
 136
 137static int __init ns_init(void)
 138{
 139        return phy_driver_register(&dp83865_driver);
 140}
 141
 142static void __exit ns_exit(void)
 143{
 144        phy_driver_unregister(&dp83865_driver);
 145}
 146
 147MODULE_DESCRIPTION("NatSemi PHY driver");
 148MODULE_AUTHOR("Stuart Menefy");
 149MODULE_LICENSE("GPL");
 150
 151module_init(ns_init);
 152module_exit(ns_exit);
 153
 154static struct mdio_device_id __maybe_unused ns_tbl[] = {
 155        { DP83865_PHY_ID, 0xfffffff0 },
 156        { }
 157};
 158
 159MODULE_DEVICE_TABLE(mdio, ns_tbl);
 160