diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 37 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/broadcom.c | 20 | ||||
-rw-r--r-- | drivers/net/phy/fixed.c | 445 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 68 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 11 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 80 |
8 files changed, 331 insertions, 333 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 54b2ba99664..f4ca0591231 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -60,35 +60,18 @@ config ICPLUS_PHY ---help--- Currently supports the IP175C PHY. +config REALTEK_PHY + tristate "Drivers for Realtek PHYs" + ---help--- + Supports the Realtek 821x PHY. + config FIXED_PHY - tristate "Drivers for PHY emulation on fixed speed/link" + bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" ---help--- - Adds the driver to PHY layer to cover the boards that do not have any PHY bound, - but with the ability to manipulate the speed/link in software. The relevant MII - speed/duplex parameters could be effectively handled in a user-specified function. - Currently tested with mpc866ads. - -config FIXED_MII_10_FDX - bool "Emulation for 10M Fdx fixed PHY behavior" - depends on FIXED_PHY - -config FIXED_MII_100_FDX - bool "Emulation for 100M Fdx fixed PHY behavior" - depends on FIXED_PHY - -config FIXED_MII_1000_FDX - bool "Emulation for 1000M Fdx fixed PHY behavior" - depends on FIXED_PHY - -config FIXED_MII_AMNT - int "Number of emulated PHYs to allocate " - depends on FIXED_PHY - default "1" - ---help--- - Sometimes it is required to have several independent emulated - PHYs on the bus (in case of multi-eth but phy-less HW for instance). - This control will have specified number allocated for each fixed - PHY type enabled. + Adds the platform "fixed" MDIO Bus to cover the boards that use + PHYs that are not connected to the real MDIO bus. + + Currently tested with mpc866ads and mpc8349e-mitx. config MDIO_BITBANG tristate "Support for bitbanged MDIO buses" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 3d6cc7b67a8..5997d6ef702 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_VITESSE_PHY) += vitesse.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o +obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_FIXED_PHY) += fixed.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 29666c85ed5..5b80358af65 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -141,6 +141,20 @@ static struct phy_driver bcm5461_driver = { .driver = { .owner = THIS_MODULE }, }; +static struct phy_driver bcm5482_driver = { + .phy_id = 0x0143bcb0, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM5482", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = bcm54xx_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm54xx_ack_interrupt, + .config_intr = bcm54xx_config_intr, + .driver = { .owner = THIS_MODULE }, +}; + static int __init broadcom_init(void) { int ret; @@ -154,8 +168,13 @@ static int __init broadcom_init(void) ret = phy_driver_register(&bcm5461_driver); if (ret) goto out_5461; + ret = phy_driver_register(&bcm5482_driver); + if (ret) + goto out_5482; return ret; +out_5482: + phy_driver_unregister(&bcm5461_driver); out_5461: phy_driver_unregister(&bcm5421_driver); out_5421: @@ -166,6 +185,7 @@ out_5411: static void __exit broadcom_exit(void) { + phy_driver_unregister(&bcm5482_driver); phy_driver_unregister(&bcm5461_driver); phy_driver_unregister(&bcm5421_driver); phy_driver_unregister(&bcm5411_driver); diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 56191822fa2..73b6d39ef6b 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -1,362 +1,253 @@ /* - * drivers/net/phy/fixed.c + * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) * - * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode. + * Author: Vitaly Bordug <vbordug@ru.mvista.com> + * Anton Vorontsov <avorontsov@ru.mvista.com> * - * Author: Vitaly Bordug - * - * Copyright (c) 2006 MontaVista Software, Inc. + * Copyright (c) 2006-2007 MontaVista Software, 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/kernel.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/platform_device.h> +#include <linux/list.h> #include <linux/mii.h> -#include <linux/ethtool.h> #include <linux/phy.h> #include <linux/phy_fixed.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> +#define MII_REGS_NUM 29 -/* we need to track the allocated pointers in order to free them on exit */ -static struct fixed_info *fixed_phy_ptrs[CONFIG_FIXED_MII_AMNT*MAX_PHY_AMNT]; - -/*----------------------------------------------------------------------------- - * If something weird is required to be done with link/speed, - * network driver is able to assign a function to implement this. - * May be useful for PHY's that need to be software-driven. - *-----------------------------------------------------------------------------*/ -int fixed_mdio_set_link_update(struct phy_device *phydev, - int (*link_update) (struct net_device *, - struct fixed_phy_status *)) -{ - struct fixed_info *fixed; - - if (link_update == NULL) - return -EINVAL; - - if (phydev) { - if (phydev->bus) { - fixed = phydev->bus->priv; - fixed->link_update = link_update; - return 0; - } - } - return -EINVAL; -} - -EXPORT_SYMBOL(fixed_mdio_set_link_update); +struct fixed_mdio_bus { + int irqs[PHY_MAX_ADDR]; + struct mii_bus mii_bus; + struct list_head phys; +}; -struct fixed_info *fixed_mdio_get_phydev (int phydev_ind) -{ - if (phydev_ind >= MAX_PHY_AMNT) - return NULL; - return fixed_phy_ptrs[phydev_ind]; -} +struct fixed_phy { + int id; + u16 regs[MII_REGS_NUM]; + struct phy_device *phydev; + struct fixed_phy_status status; + int (*link_update)(struct net_device *, struct fixed_phy_status *); + struct list_head node; +}; -EXPORT_SYMBOL(fixed_mdio_get_phydev); +static struct platform_device *pdev; +static struct fixed_mdio_bus platform_fmb = { + .phys = LIST_HEAD_INIT(platform_fmb.phys), +}; -/*----------------------------------------------------------------------------- - * This is used for updating internal mii regs from the status - *-----------------------------------------------------------------------------*/ -#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX) -static int fixed_mdio_update_regs(struct fixed_info *fixed) +static int fixed_phy_update_regs(struct fixed_phy *fp) { - u16 *regs = fixed->regs; - u16 bmsr = 0; + u16 bmsr = BMSR_ANEGCAPABLE; u16 bmcr = 0; + u16 lpagb = 0; + u16 lpa = 0; - if (!regs) { - printk(KERN_ERR "%s: regs not set up", __FUNCTION__); - return -EINVAL; - } - - if (fixed->phy_status.link) - bmsr |= BMSR_LSTATUS; - - if (fixed->phy_status.duplex) { + if (fp->status.duplex) { bmcr |= BMCR_FULLDPLX; - switch (fixed->phy_status.speed) { + switch (fp->status.speed) { + case 1000: + bmsr |= BMSR_ESTATEN; + bmcr |= BMCR_SPEED1000; + lpagb |= LPA_1000FULL; + break; case 100: bmsr |= BMSR_100FULL; bmcr |= BMCR_SPEED100; + lpa |= LPA_100FULL; break; - case 10: bmsr |= BMSR_10FULL; + lpa |= LPA_10FULL; break; + default: + printk(KERN_WARNING "fixed phy: unknown speed\n"); + return -EINVAL; } } else { - switch (fixed->phy_status.speed) { + switch (fp->status.speed) { + case 1000: + bmsr |= BMSR_ESTATEN; + bmcr |= BMCR_SPEED1000; + lpagb |= LPA_1000HALF; + break; case 100: bmsr |= BMSR_100HALF; bmcr |= BMCR_SPEED100; + lpa |= LPA_100HALF; break; - case 10: - bmsr |= BMSR_100HALF; + bmsr |= BMSR_10HALF; + lpa |= LPA_10HALF; break; + default: + printk(KERN_WARNING "fixed phy: unknown speed\n"); + return -EINVAL; } } - regs[MII_BMCR] = bmcr; - regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx */ + if (fp->status.link) + bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; + + if (fp->status.pause) + lpa |= LPA_PAUSE_CAP; + + if (fp->status.asym_pause) + lpa |= LPA_PAUSE_ASYM; + + fp->regs[MII_PHYSID1] = fp->id >> 16; + fp->regs[MII_PHYSID2] = fp->id; + + fp->regs[MII_BMSR] = bmsr; + fp->regs[MII_BMCR] = bmcr; + fp->regs[MII_LPA] = lpa; + fp->regs[MII_STAT1000] = lpagb; return 0; } -static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location) +static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) { - struct fixed_info *fixed = bus->priv; - - /* if user has registered link update callback, use it */ - if (fixed->phydev) - if (fixed->phydev->attached_dev) { - if (fixed->link_update) { - fixed->link_update(fixed->phydev->attached_dev, - &fixed->phy_status); - fixed_mdio_update_regs(fixed); + struct fixed_mdio_bus *fmb = container_of(bus, struct fixed_mdio_bus, + mii_bus); + struct fixed_phy *fp; + + if (reg_num >= MII_REGS_NUM) + return -1; + + list_for_each_entry(fp, &fmb->phys, node) { + if (fp->id == phy_id) { + /* Issue callback if user registered it. */ + if (fp->link_update) { + fp->link_update(fp->phydev->attached_dev, + &fp->status); + fixed_phy_update_regs(fp); } + return fp->regs[reg_num]; } + } - if ((unsigned int)location >= fixed->regs_num) - return -1; - return fixed->regs[location]; + return 0xFFFF; } -static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, - u16 val) +static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num, + u16 val) { - /* do nothing for now */ return 0; } -static int fixed_mii_reset(struct mii_bus *bus) +/* + * If something weird is required to be done with link/speed, + * network driver is able to assign a function to implement this. + * May be useful for PHY's that need to be software-driven. + */ +int fixed_phy_set_link_update(struct phy_device *phydev, + int (*link_update)(struct net_device *, + struct fixed_phy_status *)) { - /*nothing here - no way/need to reset it */ - return 0; -} -#endif + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp; -static int fixed_config_aneg(struct phy_device *phydev) -{ - /* :TODO:03/13/2006 09:45:37 PM:: - The full autoneg funcionality can be emulated, - but no need to have anything here for now - */ - return 0; -} + if (!link_update || !phydev || !phydev->bus) + return -EINVAL; -/*----------------------------------------------------------------------------- - * the manual bind will do the magic - with phy_id_mask == 0 - * match will never return true... - *-----------------------------------------------------------------------------*/ -static struct phy_driver fixed_mdio_driver = { - .name = "Fixed PHY", -#ifdef CONFIG_FIXED_MII_1000_FDX - .features = PHY_GBIT_FEATURES, -#else - .features = PHY_BASIC_FEATURES, -#endif - .config_aneg = fixed_config_aneg, - .read_status = genphy_read_status, - .driver = { .owner = THIS_MODULE, }, -}; + list_for_each_entry(fp, &fmb->phys, node) { + if (fp->id == phydev->phy_id) { + fp->link_update = link_update; + fp->phydev = phydev; + return 0; + } + } -static void fixed_mdio_release(struct device *dev) -{ - struct phy_device *phydev = container_of(dev, struct phy_device, dev); - struct mii_bus *bus = phydev->bus; - struct fixed_info *fixed = bus->priv; - - kfree(phydev); - kfree(bus->dev); - kfree(bus); - kfree(fixed->regs); - kfree(fixed); + return -ENOENT; } +EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -/*----------------------------------------------------------------------------- - * This func is used to create all the necessary stuff, bind - * the fixed phy driver and register all it on the mdio_bus_type. - * speed is either 10 or 100 or 1000, duplex is boolean. - * number is used to create multiple fixed PHYs, so that several devices can - * utilize them simultaneously. - * - * The device on mdio bus will look like [bus_id]:[phy_id], - * bus_id = number - * phy_id = speed+duplex. - *-----------------------------------------------------------------------------*/ -#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX) -struct fixed_info *fixed_mdio_register_device( - int bus_id, int speed, int duplex, u8 phy_id) +int fixed_phy_add(unsigned int irq, int phy_id, + struct fixed_phy_status *status) { - struct mii_bus *new_bus; - struct fixed_info *fixed; - struct phy_device *phydev; - int err; + int ret; + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp; - struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return -ENOMEM; - if (dev == NULL) - goto err_dev_alloc; + memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + fmb->irqs[phy_id] = irq; - if (new_bus == NULL) - goto err_bus_alloc; + fp->id = phy_id; + fp->status = *status; - fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL); + ret = fixed_phy_update_regs(fp); + if (ret) + goto err_regs; - if (fixed == NULL) - goto err_fixed_alloc; + list_add_tail(&fp->node, &fmb->phys); - fixed->regs = kzalloc(MII_REGS_NUM * sizeof(int), GFP_KERNEL); - if (NULL == fixed->regs) - goto err_fixed_regs_alloc; + return 0; - fixed->regs_num = MII_REGS_NUM; - fixed->phy_status.speed = speed; - fixed->phy_status.duplex = duplex; - fixed->phy_status.link = 1; +err_regs: + kfree(fp); + return ret; +} +EXPORT_SYMBOL_GPL(fixed_phy_add); - new_bus->name = "Fixed MII Bus"; - new_bus->read = &fixed_mii_read; - new_bus->write = &fixed_mii_write; - new_bus->reset = &fixed_mii_reset; - /*set up workspace */ - fixed_mdio_update_regs(fixed); - new_bus->priv = fixed; +static int __init fixed_mdio_bus_init(void) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + int ret; - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); + pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); + if (!pdev) { + ret = -ENOMEM; + goto err_pdev; + } - /* create phy_device and register it on the mdio bus */ - phydev = phy_device_create(new_bus, 0, 0); - if (phydev == NULL) - goto err_phy_dev_create; + fmb->mii_bus.id = 0; + fmb->mii_bus.name = "Fixed MDIO Bus"; + fmb->mii_bus.dev = &pdev->dev; + fmb->mii_bus.read = &fixed_mdio_read; + fmb->mii_bus.write = &fixed_mdio_write; + fmb->mii_bus.irq = fmb->irqs; - /* - * Put the phydev pointer into the fixed pack so that bus read/write - * code could be able to access for instance attached netdev. Well it - * doesn't have to do so, only in case of utilizing user-specified - * link-update... - */ + ret = mdiobus_register(&fmb->mii_bus); + if (ret) + goto err_mdiobus_reg; - fixed->phydev = phydev; - phydev->speed = speed; - phydev->duplex = duplex; + return 0; - phydev->irq = PHY_IGNORE_INTERRUPT; - phydev->dev.bus = &mdio_bus_type; +err_mdiobus_reg: + platform_device_unregister(pdev); +err_pdev: + return ret; +} +module_init(fixed_mdio_bus_init); - snprintf(phydev->dev.bus_id, BUS_ID_SIZE, - PHY_ID_FMT, bus_id, phy_id); +static void __exit fixed_mdio_bus_exit(void) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp; - phydev->bus = new_bus; + mdiobus_unregister(&fmb->mii_bus); + platform_device_unregister(pdev); - phydev->dev.driver = &fixed_mdio_driver.driver; - phydev->dev.release = fixed_mdio_release; - err = phydev->dev.driver->probe(&phydev->dev); - if (err < 0) { - printk(KERN_ERR "Phy %s: problems with fixed driver\n", - phydev->dev.bus_id); - goto err_out; - } - err = device_register(&phydev->dev); - if (err) { - printk(KERN_ERR "Phy %s failed to register\n", - phydev->dev.bus_id); - goto err_out; + list_for_each_entry(fp, &fmb->phys, node) { + list_del(&fp->node); + kfree(fp); } - //phydev->state = PHY_RUNNING; /* make phy go up quick, but in 10Mbit/HDX - return fixed; - -err_out: - kfree(phydev); -err_phy_dev_create: - kfree(fixed->regs); -err_fixed_regs_alloc: - kfree(fixed); -err_fixed_alloc: - kfree(new_bus); -err_bus_alloc: - kfree(dev); -err_dev_alloc: - - return NULL; - } -#endif +module_exit(fixed_mdio_bus_exit); -MODULE_DESCRIPTION("Fixed PHY device & driver for PAL"); +MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); MODULE_AUTHOR("Vitaly Bordug"); MODULE_LICENSE("GPL"); - -static int __init fixed_init(void) -{ - int cnt = 0; - int i; -/* register on the bus... Not expected to be matched - * with anything there... - * - */ - phy_driver_register(&fixed_mdio_driver); - -/* We will create several mdio devices here, and will bound the upper - * driver to them. - * - * Then the external software can lookup the phy bus by searching - * for 0:101, to be connected to the virtual 100M Fdx phy. - * - * In case several virtual PHYs required, the bus_id will be in form - * [num]:[duplex]+[speed], which make it able even to define - * driver-specific link control callback, if for instance PHY is - * completely SW-driven. - */ - for (i=1; i <= CONFIG_FIXED_MII_AMNT; i++) { -#ifdef CONFIG_FIXED_MII_1000_FDX - fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(0, 1000, 1, i); -#endif -#ifdef CONFIG_FIXED_MII_100_FDX - fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(1, 100, 1, i); -#endif -#ifdef CONFIG_FIXED_MII_10_FDX - fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(2, 10, 1, i); -#endif - } - - return 0; -} - -static void __exit fixed_exit(void) -{ - int i; - - phy_driver_unregister(&fixed_mdio_driver); - for (i=0; i < MAX_PHY_AMNT; i++) - if ( fixed_phy_ptrs[i] ) - device_unregister(&fixed_phy_ptrs[i]->phydev->dev); -} - -module_init(fixed_init); -module_exit(fixed_exit); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c30196d0ad1..6e9f619c491 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -49,7 +49,7 @@ int mdiobus_register(struct mii_bus *bus) int i; int err = 0; - spin_lock_init(&bus->mdio_lock); + mutex_init(&bus->mdio_lock); if (NULL == bus || NULL == bus->name || NULL == bus->read || diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7c9e6e34950..12fccb1c76d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -26,7 +26,6 @@ #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/mii.h> @@ -72,9 +71,11 @@ int phy_read(struct phy_device *phydev, u16 regnum) int retval; struct mii_bus *bus = phydev->bus; - spin_lock_bh(&bus->mdio_lock); + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); retval = bus->read(bus, phydev->addr, regnum); - spin_unlock_bh(&bus->mdio_lock); + mutex_unlock(&bus->mdio_lock); return retval; } @@ -95,9 +96,11 @@ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) int err; struct mii_bus *bus = phydev->bus; - spin_lock_bh(&bus->mdio_lock); + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); err = bus->write(bus, phydev->addr, regnum, val); - spin_unlock_bh(&bus->mdio_lock); + mutex_unlock(&bus->mdio_lock); return err; } @@ -428,7 +431,7 @@ int phy_start_aneg(struct phy_device *phydev) { int err; - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); if (AUTONEG_DISABLE == phydev->autoneg) phy_sanitize_settings(phydev); @@ -449,13 +452,14 @@ int phy_start_aneg(struct phy_device *phydev) } out_unlock: - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); return err; } EXPORT_SYMBOL(phy_start_aneg); static void phy_change(struct work_struct *work); +static void phy_state_machine(struct work_struct *work); static void phy_timer(unsigned long data); /** @@ -476,6 +480,7 @@ void phy_start_machine(struct phy_device *phydev, { phydev->adjust_state = handler; + INIT_WORK(&phydev->state_queue, phy_state_machine); init_timer(&phydev->phy_timer); phydev->phy_timer.function = &phy_timer; phydev->phy_timer.data = (unsigned long) phydev; @@ -493,11 +498,12 @@ void phy_start_machine(struct phy_device *phydev, void phy_stop_machine(struct phy_device *phydev) { del_timer_sync(&phydev->phy_timer); + cancel_work_sync(&phydev->state_queue); - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); if (phydev->state > PHY_UP) phydev->state = PHY_UP; - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); phydev->adjust_state = NULL; } @@ -541,9 +547,9 @@ static void phy_force_reduction(struct phy_device *phydev) */ void phy_error(struct phy_device *phydev) { - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); phydev->state = PHY_HALTED; - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); } /** @@ -705,10 +711,10 @@ static void phy_change(struct work_struct *work) if (err) goto phy_err; - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) phydev->state = PHY_CHANGELINK; - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); atomic_dec(&phydev->irq_disable); enable_irq(phydev->irq); @@ -735,7 +741,7 @@ phy_err: */ void phy_stop(struct phy_device *phydev) { - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); if (PHY_HALTED == phydev->state) goto out_unlock; @@ -751,7 +757,7 @@ void phy_stop(struct phy_device *phydev) phydev->state = PHY_HALTED; out_unlock: - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); /* * Cannot call flush_scheduled_work() here as desired because @@ -773,7 +779,7 @@ out_unlock: */ void phy_start(struct phy_device *phydev) { - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); switch (phydev->state) { case PHY_STARTING: @@ -787,19 +793,26 @@ void phy_start(struct phy_device *phydev) default: break; } - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); } EXPORT_SYMBOL(phy_stop); EXPORT_SYMBOL(phy_start); -/* PHY timer which handles the state machine */ -static void phy_timer(unsigned long data) +/** + * phy_state_machine - Handle the state machine + * @work: work_struct that describes the work to be done + * + * Description: Scheduled by the state_queue workqueue each time + * phy_timer is triggered. + */ +static void phy_state_machine(struct work_struct *work) { - struct phy_device *phydev = (struct phy_device *)data; + struct phy_device *phydev = + container_of(work, struct phy_device, state_queue); int needs_aneg = 0; int err = 0; - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); if (phydev->adjust_state) phydev->adjust_state(phydev->attached_dev); @@ -965,7 +978,7 @@ static void phy_timer(unsigned long data) break; } - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); if (needs_aneg) err = phy_start_aneg(phydev); @@ -976,3 +989,14 @@ static void phy_timer(unsigned long data) mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ); } +/* PHY timer which schedules the state machine work */ +static void phy_timer(unsigned long data) +{ + struct phy_device *phydev = (struct phy_device *)data; + + /* + * PHY I/O operations can potentially sleep so we ensure that + * it's done from a process context + */ + schedule_work(&phydev->state_queue); +} diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5b9e1751e1b..f4c4fd85425 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -25,7 +25,6 @@ #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/mii.h> @@ -80,7 +79,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) dev->state = PHY_DOWN; - spin_lock_init(&dev->lock); + mutex_init(&dev->lock); return dev; } @@ -656,7 +655,7 @@ static int phy_probe(struct device *dev) if (!(phydrv->flags & PHY_HAS_INTERRUPT)) phydev->irq = PHY_POLL; - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); /* Start out supporting everything. Eventually, * a controller will attach, and may modify one @@ -670,7 +669,7 @@ static int phy_probe(struct device *dev) if (phydev->drv->probe) err = phydev->drv->probe(phydev); - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); return err; @@ -682,9 +681,9 @@ static int phy_remove(struct device *dev) phydev = to_phy_device(dev); - spin_lock_bh(&phydev->lock); + mutex_lock(&phydev->lock); phydev->state = PHY_DOWN; - spin_unlock_bh(&phydev->lock); + mutex_unlock(&phydev->lock); if (phydev->drv->remove) phydev->drv->remove(phydev); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c new file mode 100644 index 00000000000..a052a6744a5 --- /dev/null +++ b/drivers/net/phy/realtek.c @@ -0,0 +1,80 @@ +/* + * drivers/net/phy/realtek.c + * + * Driver for Realtek PHYs + * + * Author: Johnson Leung <r58129@freescale.com> + * + * 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/phy.h> + +#define RTL821x_PHYSR 0x11 +#define RTL821x_PHYSR_DUPLEX 0x2000 +#define RTL821x_PHYSR_SPEED 0xc000 +#define RTL821x_INER 0x12 +#define RTL821x_INER_INIT 0x6400 +#define RTL821x_INSR 0x13 + +MODULE_DESCRIPTION("Realtek PHY driver"); +MODULE_AUTHOR("Johnson Leung"); +MODULE_LICENSE("GPL"); + +static int rtl821x_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, RTL821x_INSR); + + return (err < 0) ? err : 0; +} + +static int rtl821x_config_intr(struct phy_device *phydev) +{ + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + err = phy_write(phydev, RTL821x_INER, + RTL821x_INER_INIT); + else + err = phy_write(phydev, RTL821x_INER, 0); + + return err; +} + +/* RTL8211B */ +static struct phy_driver rtl821x_driver = { + .phy_id = 0x001cc912, + .name = "RTL821x Gigabit Ethernet", + .phy_id_mask = 0x001fffff, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &rtl821x_ack_interrupt, + .config_intr = &rtl821x_config_intr, + .driver = { .owner = THIS_MODULE,}, +}; + +static int __init realtek_init(void) +{ + int ret; + + ret = phy_driver_register(&rtl821x_driver); + + return ret; +} + +static void __exit realtek_exit(void) +{ + phy_driver_unregister(&rtl821x_driver); +} + +module_init(realtek_init); +module_exit(realtek_exit); |