Commit 00db8189 authored by Andy Fleming's avatar Andy Fleming Committed by Jeff Garzik

This patch adds a PHY Abstraction Layer to the Linux Kernel, enabling

ethernet drivers to remain as ignorant as is reasonable of the connected
PHY's design and operation details.
Signed-off-by: default avatarAndy Fleming <afleming@freescale.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent b0825488
This diff is collapsed.
......@@ -131,6 +131,8 @@ config NET_SB1000
source "drivers/net/arcnet/Kconfig"
source "drivers/net/phy/Kconfig"
#
# Ethernet
#
......
......@@ -65,6 +65,7 @@ obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
#
obj-$(CONFIG_MII) += mii.o
obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_SUNDANCE) += sundance.o
obj-$(CONFIG_HAMACHI) += hamachi.o
......
#
# PHY Layer Configuration
#
menu "PHY device support"
config PHYLIB
bool "PHY Device support and infrastructure"
depends on NET_ETHERNET
help
Ethernet controllers are usually attached to PHY
devices. This option provides infrastructure for
managing PHY devices.
config PHYCONTROL
bool "Support for automatically handling PHY state changes"
depends on PHYLIB
help
Adds code to perform all the work for keeping PHY link
state (speed/duplex/etc) up-to-date. Also handles
interrupts.
comment "MII PHY device drivers"
depends on PHYLIB
config MARVELL_PHY
bool "Drivers for Marvell PHYs"
depends on PHYLIB
---help---
Currently has a driver for the 88E1011S
config DAVICOM_PHY
bool "Drivers for Davicom PHYs"
depends on PHYLIB
---help---
Currently supports dm9161e and dm9131
config QSEMI_PHY
bool "Drivers for Quality Semiconductor PHYs"
depends on PHYLIB
---help---
Currently supports the qs6612
config LXT_PHY
bool "Drivers for the Intel LXT PHYs"
depends on PHYLIB
---help---
Currently supports the lxt970, lxt971
config CICADA_PHY
bool "Drivers for the Cicada PHYs"
depends on PHYLIB
---help---
Currently supports the cis8204
endmenu
# Makefile for Linux PHY drivers
obj-$(CONFIG_PHYLIB) += phy.o phy_device.o mdio_bus.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
obj-$(CONFIG_CICADA_PHY) += cicada.o
obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
/*
* drivers/net/phy/cicada.c
*
* Driver for Cicada PHYs
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* Cicada Extended Control Register 1 */
#define MII_CIS8201_EXT_CON1 0x17
#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
#define MII_CIS8201_IMASK 0x19
#define MII_CIS8201_IMASK_IEN 0x8000
#define MII_CIS8201_IMASK_SPEED 0x4000
#define MII_CIS8201_IMASK_LINK 0x2000
#define MII_CIS8201_IMASK_DUPLEX 0x1000
#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
#define MII_CIS8201_ISTAT 0x1a
#define MII_CIS8201_ISTAT_STATUS 0x8000
#define MII_CIS8201_ISTAT_SPEED 0x4000
#define MII_CIS8201_ISTAT_LINK 0x2000
#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
#define MII_CIS8201_AUX_CONSTAT 0x1c
#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
#define MII_CIS8201_AUXCONSTAT_100 0x0008
MODULE_DESCRIPTION("Cicadia PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
static int cis820x_config_init(struct phy_device *phydev)
{
int err;
err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
MII_CIS8201_AUXCONSTAT_INIT);
if (err < 0)
return err;
err = phy_write(phydev, MII_CIS8201_EXT_CON1,
MII_CIS8201_EXTCON1_INIT);
return err;
}
static int cis820x_ack_interrupt(struct phy_device *phydev)
{
int err = phy_read(phydev, MII_CIS8201_ISTAT);
return (err < 0) ? err : 0;
}
static int cis820x_config_intr(struct phy_device *phydev)
{
int err;
if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, MII_CIS8201_IMASK,
MII_CIS8201_IMASK_MASK);
else
err = phy_write(phydev, MII_CIS8201_IMASK, 0);
return err;
}
/* Cicada 820x */
static struct phy_driver cis8204_driver = {
.phy_id = 0x000fc440,
.name = "Cicada Cis8204",
.phy_id_mask = 0x000fffc0,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = &cis820x_config_init,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static int __init cis8204_init(void)
{
return phy_driver_register(&cis8204_driver);
}
static void __exit cis8204_exit(void)
{
phy_driver_unregister(&cis8204_driver);
}
module_init(cis8204_init);
module_exit(cis8204_exit);
/*
* drivers/net/phy/davicom.c
*
* Driver for Davicom PHYs
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#define MII_DM9161_SCR 0x10
#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Interrupt Register */
#define MII_DM9161_INTR 0x15
#define MII_DM9161_INTR_PEND 0x8000
#define MII_DM9161_INTR_DPLX_MASK 0x0800
#define MII_DM9161_INTR_SPD_MASK 0x0400
#define MII_DM9161_INTR_LINK_MASK 0x0200
#define MII_DM9161_INTR_MASK 0x0100
#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
#define MII_DM9161_INTR_SPD_CHANGE 0x0008
#define MII_DM9161_INTR_LINK_CHANGE 0x0004
#define MII_DM9161_INTR_INIT 0x0000
#define MII_DM9161_INTR_STOP \
(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
| MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
/* DM9161 10BT Configuration/Status */
#define MII_DM9161_10BTCSR 0x12
#define MII_DM9161_10BTCSR_INIT 0x7800
MODULE_DESCRIPTION("Davicom PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
#define DM9161_DELAY 1
static int dm9161_config_intr(struct phy_device *phydev)
{
int temp;
temp = phy_read(phydev, MII_DM9161_INTR);
if (temp < 0)
return temp;
if(PHY_INTERRUPT_ENABLED == phydev->interrupts )
temp &= ~(MII_DM9161_INTR_STOP);
else
temp |= MII_DM9161_INTR_STOP;
temp = phy_write(phydev, MII_DM9161_INTR, temp);
return temp;
}
static int dm9161_config_aneg(struct phy_device *phydev)
{
int err;
/* Isolate the PHY */
err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
if (err < 0)
return err;
/* Configure the new settings */
err = genphy_config_aneg(phydev);
if (err < 0)
return err;
return 0;
}
static int dm9161_config_init(struct phy_device *phydev)
{
int err;
/* Isolate the PHY */
err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
if (err < 0)
return err;
/* Do not bypass the scrambler/descrambler */
err = phy_write(phydev, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
if (err < 0)
return err;
/* Clear 10BTCSR to default */
err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
if (err < 0)
return err;
/* Reconnect the PHY, and enable Autonegotiation */
err = phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
if (err < 0)
return err;
return 0;
}
static int dm9161_ack_interrupt(struct phy_device *phydev)
{
int err = phy_read(phydev, MII_DM9161_INTR);
return (err < 0) ? err : 0;
}
static struct phy_driver dm9161_driver = {
.phy_id = 0x0181b880,
.name = "Davicom DM9161E",
.phy_id_mask = 0x0ffffff0,
.features = PHY_BASIC_FEATURES,
.config_init = dm9161_config_init,
.config_aneg = dm9161_config_aneg,
.read_status = genphy_read_status,
.driver = { .owner = THIS_MODULE,},
};
static struct phy_driver dm9131_driver = {
.phy_id = 0x00181b80,
.name = "Davicom DM9131",
.phy_id_mask = 0x0ffffff0,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static int __init davicom_init(void)
{
int ret;
ret = phy_driver_register(&dm9161_driver);
if (ret)
goto err1;
ret = phy_driver_register(&dm9131_driver);
if (ret)
goto err2;
return 0;
err2:
phy_driver_unregister(&dm9161_driver);
err1:
return ret;
}
static void __exit davicom_exit(void)
{
phy_driver_unregister(&dm9161_driver);
phy_driver_unregister(&dm9131_driver);
}
module_init(davicom_init);
module_exit(davicom_exit);
/*
* drivers/net/phy/lxt.c
*
* Driver for Intel LXT PHYs
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* The Level one LXT970 is used by many boards */
#define MII_LXT970_IER 17 /* Interrupt Enable Register */
#define MII_LXT970_IER_IEN 0x0002
#define MII_LXT970_ISR 18 /* Interrupt Status Register */
#define MII_LXT970_CONFIG 19 /* Configuration Register */
/* ------------------------------------------------------------------------- */
/* The Level one LXT971 is used on some of my custom boards */
/* register definitions for the 971 */
#define MII_LXT971_IER 18 /* Interrupt Enable Register */
#define MII_LXT971_IER_IEN 0x00f2
#define MII_LXT971_ISR 19 /* Interrupt Status Register */
MODULE_DESCRIPTION("Intel LXT PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
static int lxt970_ack_interrupt(struct phy_device *phydev)
{
int err;
err = phy_read(phydev, MII_BMSR);
if (err < 0)
return err;
err = phy_read(phydev, MII_LXT970_ISR);
if (err < 0)
return err;
return 0;
}
static int lxt970_config_intr(struct phy_device *phydev)
{
int err;
if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
else
err = phy_write(phydev, MII_LXT970_IER, 0);
return err;
}
static int lxt970_config_init(struct phy_device *phydev)
{
int err;
err = phy_write(phydev, MII_LXT970_CONFIG, 0);
return err;
}
static int lxt971_ack_interrupt(struct phy_device *phydev)
{
int err = phy_read(phydev, MII_LXT971_ISR);
if (err < 0)
return err;
return 0;
}
static int lxt971_config_intr(struct phy_device *phydev)
{
int err;
if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
else
err = phy_write(phydev, MII_LXT971_IER, 0);
return err;
}
static struct phy_driver lxt970_driver = {
.phy_id = 0x07810000,
.name = "LXT970",
.phy_id_mask = 0x0fffffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = lxt970_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = lxt970_ack_interrupt,
.config_intr = lxt970_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static struct phy_driver lxt971_driver = {
.phy_id = 0x0001378e,
.name = "LXT971",
.phy_id_mask = 0x0fffffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = lxt971_ack_interrupt,
.config_intr = lxt971_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static int __init lxt_init(void)
{
int ret;
ret = phy_driver_register(&lxt970_driver);
if (ret)
goto err1;
ret = phy_driver_register(&lxt971_driver);
if (ret)
goto err2;
return 0;
err2:
phy_driver_unregister(&lxt970_driver);
err1:
return ret;
}
static void __exit lxt_exit(void)
{
phy_driver_unregister(&lxt970_driver);
phy_driver_unregister(&lxt971_driver);
}
module_init(lxt_init);
module_exit(lxt_exit);
/*
* drivers/net/phy/marvell.c
*
* Driver for Marvell PHYs
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#define MII_M1011_IEVENT 0x13
#define MII_M1011_IEVENT_CLEAR 0x0000
#define MII_M1011_IMASK 0x12
#define MII_M1011_IMASK_INIT 0x6400
#define MII_M1011_IMASK_CLEAR 0x0000
MODULE_DESCRIPTION("Marvell PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
static int marvell_ack_interrupt(struct phy_device *phydev)
{
int err;
/* Clear the interrupts by reading the reg */
err = phy_read(phydev, MII_M1011_IEVENT);
if (err < 0)
return err;
return 0;
}
static int marvell_config_intr(struct phy_device *phydev)
{
int err;
if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else
err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
return err;
}
static int marvell_config_aneg(struct phy_device *phydev)
{
int err;
/* The Marvell PHY has an errata which requires
* that certain registers get written in order
* to restart autonegotiation */
err = phy_write(phydev, MII_BMCR, BMCR_RESET);
if (err < 0)
return err;
err = phy_write(phydev, 0x1d, 0x1f);
if (err < 0)
return err;
err = phy_write(phydev, 0x1e, 0x200c);
if (err < 0)
return err;
err = phy_write(phydev, 0x1d, 0x5);
if (err < 0)
return err;
err = phy_write(phydev, 0x1e, 0);
if (err < 0)
return err;
err = phy_write(phydev, 0x1e, 0x100);
if (err < 0)
return err;
err = genphy_config_aneg(phydev);
return err;
}
static struct phy_driver m88e1101_driver = {
.phy_id = 0x01410c00,
.phy_id_mask = 0xffffff00,
.name = "Marvell 88E1101",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &marvell_ack_interrupt,
.config_intr = &marvell_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static int __init marvell_init(void)
{
return phy_driver_register(&m88e1101_driver);
}
static void __exit marvell_exit(void)
{
phy_driver_unregister(&m88e1101_driver);
}
module_init(marvell_init);
module_exit(marvell_exit);
/*
* drivers/net/phy/mdio_bus.c
*
* MDIO Bus interface
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* mdiobus_register
*
* description: Called by a bus driver to bring up all the PHYs
* on a given bus, and attach them to the bus
*/
int mdiobus_register(struct mii_bus *bus)
{
int i;
int err = 0;
spin_lock_init(&bus->mdio_lock);
if (NULL == bus || NULL == bus->name ||
NULL == bus->read ||
NULL == bus->write)
return -EINVAL;
if (bus->reset)
bus->reset(bus);
for (i = 0; i < PHY_MAX_ADDR; i++) {
struct phy_device *phydev;
phydev = get_phy_device(bus, i);
if (IS_ERR(phydev))
return PTR_ERR(phydev);
/* There's a PHY at this address
* We need to set:
* 1) IRQ
* 2) bus_id
* 3) parent
* 4) bus
* 5) mii_bus
* And, we need to register it */
if (phydev) {
phydev->irq = bus->irq[i];
phydev->dev.parent = bus->dev;
phydev->dev.bus = &mdio_bus_type;
sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id, i);
phydev->bus = bus;
err = device_register(&phydev->dev);
if (err)
printk(KERN_ERR "phy %d failed to register\n",
i);
}
bus->phy_map[i] = phydev;
}
pr_info("%s: probed\n", bus->name);
return err;
}
EXPORT_SYMBOL(mdiobus_register);
void mdiobus_unregister(struct mii_bus *bus)
{
int i;
for (i = 0; i < PHY_MAX_ADDR; i++) {
if (bus->phy_map[i]) {
device_unregister(&bus->phy_map[i]->dev);
kfree(bus->phy_map[i]);
}
}
}
EXPORT_SYMBOL(mdiobus_unregister);
/* mdio_bus_match
*
* description: Given a PHY device, and a PHY driver, return 1 if
* the driver supports the device. Otherwise, return 0
*/
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct phy_device *phydev = to_phy_device(dev);
struct phy_driver *phydrv = to_phy_driver(drv);
return (phydrv->phy_id == (phydev->phy_id & phydrv->phy_id_mask));
}
/* Suspend and resume. Copied from platform_suspend and
* platform_resume
*/
static int mdio_bus_suspend(struct device * dev, u32 state)
{
int ret = 0;
struct device_driver *drv = dev->driver;
if (drv && drv->suspend) {
ret = drv->suspend(dev, state, SUSPEND_DISABLE);
if (ret == 0)
ret = drv->suspend(dev, state, SUSPEND_SAVE_STATE);
if (ret == 0)
ret = drv->suspend(dev, state, SUSPEND_POWER_DOWN);
}
return ret;
}
static int mdio_bus_resume(struct device * dev)
{
int ret = 0;
struct device_driver *drv = dev->driver;
if (drv && drv->resume) {
ret = drv->resume(dev, RESUME_POWER_ON);
if (ret == 0)
ret = drv->resume(dev, RESUME_RESTORE_STATE);
if (ret == 0)
ret = drv->resume(dev, RESUME_ENABLE);
}
return ret;
}
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.suspend = mdio_bus_suspend,
.resume = mdio_bus_resume,
};
static int __init mdio_bus_init(void)
{
return bus_register(&mdio_bus_type);
}
subsys_initcall(mdio_bus_init);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* drivers/net/phy/qsemi.c
*
* Driver for Quality Semiconductor PHYs
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* ------------------------------------------------------------------------- */
/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
/* register definitions */
#define MII_QS6612_MCR 17 /* Mode Control Register */
#define MII_QS6612_FTR 27 /* Factory Test Register */
#define MII_QS6612_MCO 28 /* Misc. Control Register */
#define MII_QS6612_ISR 29 /* Interrupt Source Register */
#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
#define MII_QS6612_IMR_INIT 0x003a
#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
#define QS6612_PCR_AN_COMPLETE 0x1000
#define QS6612_PCR_RLBEN 0x0200
#define QS6612_PCR_DCREN 0x0100
#define QS6612_PCR_4B5BEN 0x0040
#define QS6612_PCR_TX_ISOLATE 0x0020
#define QS6612_PCR_MLT3_DIS 0x0002
#define QS6612_PCR_SCRM_DESCRM 0x0001
MODULE_DESCRIPTION("Quality Semiconductor PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
/* Returns 0, unless there's a write error */
static int qs6612_config_init(struct phy_device *phydev)
{
/* The PHY powers up isolated on the RPX,
* so send a command to allow operation.
* XXX - My docs indicate this should be 0x0940
* ...or something. The current value sets three
* reserved bits, bit 11, which specifies it should be
* set to one, bit 10, which specifies it should be set
* to 0, and bit 7, which doesn't specify. However, my
* docs are preliminary, and I will leave it like this
* until someone more knowledgable corrects me or it.
* -- Andy Fleming
*/
return phy_write(phydev, MII_QS6612_PCR, 0x0dc0);
}
static int qs6612_ack_interrupt(struct phy_device *phydev)
{
int err;
err = phy_read(phydev, MII_QS6612_ISR);
if (err < 0)
return err;
err = phy_read(phydev, MII_BMSR);
if (err < 0)
return err;
err = phy_read(phydev, MII_EXPANSION);
if (err < 0)
return err;
return 0;
}
static int qs6612_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, MII_QS6612_IMR,
MII_QS6612_IMR_INIT);
else
err = phy_write(phydev, MII_QS6612_IMR, 0);
return err;
}
static struct phy_driver qs6612_driver = {
.phy_id = 0x00181440,
.name = "QS6612",
.phy_id_mask = 0xfffffff0,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = qs6612_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = qs6612_ack_interrupt,
.config_intr = qs6612_config_intr,
.driver = { .owner = THIS_MODULE,},
};
static int __init qs6612_init(void)
{
return phy_driver_register(&qs6612_driver);
}
static void __exit qs6612_exit(void)
{
phy_driver_unregister(&qs6612_driver);
}
module_init(qs6612_init);
module_exit(qs6612_exit);
......@@ -408,6 +408,8 @@ struct ethtool_ops {
#define SUPPORTED_FIBRE (1 << 10)
#define SUPPORTED_BNC (1 << 11)
#define SUPPORTED_10000baseT_Full (1 << 12)
#define SUPPORTED_Pause (1 << 13)
#define SUPPORTED_Asym_Pause (1 << 14)
/* Indicates what features are advertised by the interface. */
#define ADVERTISED_10baseT_Half (1 << 0)
......@@ -423,6 +425,8 @@ struct ethtool_ops {
#define ADVERTISED_FIBRE (1 << 10)
#define ADVERTISED_BNC (1 << 11)
#define ADVERTISED_10000baseT_Full (1 << 12)
#define ADVERTISED_Pause (1 << 13)
#define ADVERTISED_Asym_Pause (1 << 14)
/* The following are all involved in forcing a particular link
* mode for the device for setting things. When getting the
......
......@@ -22,6 +22,7 @@
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_CTRL1000 0x09 /* 1000BASE-T control */
#define MII_STAT1000 0x0a /* 1000BASE-T status */
#define MII_ESTATUS 0x0f /* Extended Status */
#define MII_DCOUNTER 0x12 /* Disconnect counter */
#define MII_FCSCOUNTER 0x13 /* False carrier counter */
#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
......@@ -54,7 +55,10 @@
#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
#define BMSR_RFAULT 0x0010 /* Remote fault detected */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
#define BMSR_RESV 0x07c0 /* Unused... */
#define BMSR_RESV 0x00c0 /* Unused... */
#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */
#define BMSR_100FULL2 0x0200 /* Can do 100BASE-T2 HDX */
#define BMSR_100HALF2 0x0400 /* Can do 100BASE-T2 FDX */
#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
......@@ -114,6 +118,9 @@
#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
#define EXPANSION_RESV 0xffe0 /* Unused... */
#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */
#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */
/* N-way test register. */
#define NWAYTEST_RESV1 0x00ff /* Unused... */
#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment