linux/drivers/net/phy/davicom.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/davicom.c
   3 *
   4 * Driver for Davicom PHYs
   5 *
   6 * Author: Andy Fleming
   7 *
   8 * Copyright (c) 2004 Freescale Semiconductor, Inc.
   9 *
  10 * This program is free software; you can redistribute  it and/or modify it
  11 * under  the terms of  the GNU General  Public License as published by the
  12 * Free Software Foundation;  either version 2 of the  License, or (at your
  13 * option) any later version.
  14 *
  15 */
  16#include <linux/kernel.h>
  17#include <linux/string.h>
  18#include <linux/errno.h>
  19#include <linux/unistd.h>
  20#include <linux/interrupt.h>
  21#include <linux/init.h>
  22#include <linux/delay.h>
  23#include <linux/netdevice.h>
  24#include <linux/etherdevice.h>
  25#include <linux/skbuff.h>
  26#include <linux/spinlock.h>
  27#include <linux/mm.h>
  28#include <linux/module.h>
  29#include <linux/mii.h>
  30#include <linux/ethtool.h>
  31#include <linux/phy.h>
  32
  33#include <asm/io.h>
  34#include <asm/irq.h>
  35#include <asm/uaccess.h>
  36
  37#define MII_DM9161_SCR          0x10
  38#define MII_DM9161_SCR_INIT     0x0610
  39#define MII_DM9161_SCR_RMII     0x0100
  40
  41/* DM9161 Interrupt Register */
  42#define MII_DM9161_INTR 0x15
  43#define MII_DM9161_INTR_PEND            0x8000
  44#define MII_DM9161_INTR_DPLX_MASK       0x0800
  45#define MII_DM9161_INTR_SPD_MASK        0x0400
  46#define MII_DM9161_INTR_LINK_MASK       0x0200
  47#define MII_DM9161_INTR_MASK            0x0100
  48#define MII_DM9161_INTR_DPLX_CHANGE     0x0010
  49#define MII_DM9161_INTR_SPD_CHANGE      0x0008
  50#define MII_DM9161_INTR_LINK_CHANGE     0x0004
  51#define MII_DM9161_INTR_INIT            0x0000
  52#define MII_DM9161_INTR_STOP    \
  53(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
  54 | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
  55
  56/* DM9161 10BT Configuration/Status */
  57#define MII_DM9161_10BTCSR      0x12
  58#define MII_DM9161_10BTCSR_INIT 0x7800
  59
  60MODULE_DESCRIPTION("Davicom PHY driver");
  61MODULE_AUTHOR("Andy Fleming");
  62MODULE_LICENSE("GPL");
  63
  64
  65#define DM9161_DELAY 1
  66static int dm9161_config_intr(struct phy_device *phydev)
  67{
  68        int temp;
  69
  70        temp = phy_read(phydev, MII_DM9161_INTR);
  71
  72        if (temp < 0)
  73                return temp;
  74
  75        if(PHY_INTERRUPT_ENABLED == phydev->interrupts )
  76                temp &= ~(MII_DM9161_INTR_STOP);
  77        else
  78                temp |= MII_DM9161_INTR_STOP;
  79
  80        temp = phy_write(phydev, MII_DM9161_INTR, temp);
  81
  82        return temp;
  83}
  84
  85static int dm9161_config_aneg(struct phy_device *phydev)
  86{
  87        int err;
  88
  89        /* Isolate the PHY */
  90        err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
  91
  92        if (err < 0)
  93                return err;
  94
  95        /* Configure the new settings */
  96        err = genphy_config_aneg(phydev);
  97
  98        if (err < 0)
  99                return err;
 100
 101        return 0;
 102}
 103
 104static int dm9161_config_init(struct phy_device *phydev)
 105{
 106        int err, temp;
 107
 108        /* Isolate the PHY */
 109        err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
 110
 111        if (err < 0)
 112                return err;
 113
 114        switch (phydev->interface) {
 115        case PHY_INTERFACE_MODE_MII:
 116                temp = MII_DM9161_SCR_INIT;
 117                break;
 118        case PHY_INTERFACE_MODE_RMII:
 119                temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
 120                break;
 121        default:
 122                return -EINVAL;
 123        }
 124
 125        /* Do not bypass the scrambler/descrambler */
 126        err = phy_write(phydev, MII_DM9161_SCR, temp);
 127        if (err < 0)
 128                return err;
 129
 130        /* Clear 10BTCSR to default */
 131        err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
 132
 133        if (err < 0)
 134                return err;
 135
 136        /* Reconnect the PHY, and enable Autonegotiation */
 137        return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
 138}
 139
 140static int dm9161_ack_interrupt(struct phy_device *phydev)
 141{
 142        int err = phy_read(phydev, MII_DM9161_INTR);
 143
 144        return (err < 0) ? err : 0;
 145}
 146
 147static struct phy_driver dm91xx_driver[] = {
 148{
 149        .phy_id         = 0x0181b880,
 150        .name           = "Davicom DM9161E",
 151        .phy_id_mask    = 0x0ffffff0,
 152        .features       = PHY_BASIC_FEATURES,
 153        .config_init    = dm9161_config_init,
 154        .config_aneg    = dm9161_config_aneg,
 155        .read_status    = genphy_read_status,
 156        .driver         = { .owner = THIS_MODULE,},
 157}, {
 158        .phy_id         = 0x0181b8a0,
 159        .name           = "Davicom DM9161A",
 160        .phy_id_mask    = 0x0ffffff0,
 161        .features       = PHY_BASIC_FEATURES,
 162        .config_init    = dm9161_config_init,
 163        .config_aneg    = dm9161_config_aneg,
 164        .read_status    = genphy_read_status,
 165        .driver         = { .owner = THIS_MODULE,},
 166}, {
 167        .phy_id         = 0x00181b80,
 168        .name           = "Davicom DM9131",
 169        .phy_id_mask    = 0x0ffffff0,
 170        .features       = PHY_BASIC_FEATURES,
 171        .flags          = PHY_HAS_INTERRUPT,
 172        .config_aneg    = genphy_config_aneg,
 173        .read_status    = genphy_read_status,
 174        .ack_interrupt  = dm9161_ack_interrupt,
 175        .config_intr    = dm9161_config_intr,
 176        .driver         = { .owner = THIS_MODULE,},
 177} };
 178
 179static int __init davicom_init(void)
 180{
 181        return phy_drivers_register(dm91xx_driver,
 182                ARRAY_SIZE(dm91xx_driver));
 183}
 184
 185static void __exit davicom_exit(void)
 186{
 187        phy_drivers_unregister(dm91xx_driver,
 188                ARRAY_SIZE(dm91xx_driver));
 189}
 190
 191module_init(davicom_init);
 192module_exit(davicom_exit);
 193
 194static struct mdio_device_id __maybe_unused davicom_tbl[] = {
 195        { 0x0181b880, 0x0ffffff0 },
 196        { 0x0181b8a0, 0x0ffffff0 },
 197        { 0x00181b80, 0x0ffffff0 },
 198        { }
 199};
 200
 201MODULE_DEVICE_TABLE(mdio, davicom_tbl);
 202