123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- * drivers/net/phy/fixed.c
- *
- * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode.
- *
- * Author: Vitaly Bordug
- *
- * Copyright (c) 2006 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/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>
- /* 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_info *fixed_mdio_get_phydev (int phydev_ind)
- {
- if (phydev_ind >= MAX_PHY_AMNT)
- return NULL;
- return fixed_phy_ptrs[phydev_ind];
- }
- EXPORT_SYMBOL(fixed_mdio_get_phydev);
- /*-----------------------------------------------------------------------------
- * 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)
- {
- u16 *regs = fixed->regs;
- u16 bmsr = 0;
- u16 bmcr = 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) {
- bmcr |= BMCR_FULLDPLX;
- switch (fixed->phy_status.speed) {
- case 100:
- bmsr |= BMSR_100FULL;
- bmcr |= BMCR_SPEED100;
- break;
- case 10:
- bmsr |= BMSR_10FULL;
- break;
- }
- } else {
- switch (fixed->phy_status.speed) {
- case 100:
- bmsr |= BMSR_100HALF;
- bmcr |= BMCR_SPEED100;
- break;
- case 10:
- bmsr |= BMSR_100HALF;
- break;
- }
- }
- regs[MII_BMCR] = bmcr;
- regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx */
- return 0;
- }
- static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
- {
- 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);
- }
- }
- if ((unsigned int)location >= fixed->regs_num)
- return -1;
- return fixed->regs[location];
- }
- static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location,
- u16 val)
- {
- /* do nothing for now */
- return 0;
- }
- static int fixed_mii_reset(struct mii_bus *bus)
- {
- /*nothing here - no way/need to reset it */
- return 0;
- }
- #endif
- 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;
- }
- /*-----------------------------------------------------------------------------
- * 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, },
- };
- 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);
- }
- /*-----------------------------------------------------------------------------
- * 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)
- {
- struct mii_bus *new_bus;
- struct fixed_info *fixed;
- struct phy_device *phydev;
- int err;
- struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (dev == NULL)
- goto err_dev_alloc;
- new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
- if (new_bus == NULL)
- goto err_bus_alloc;
- fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
- if (fixed == NULL)
- goto err_fixed_alloc;
- fixed->regs = kzalloc(MII_REGS_NUM * sizeof(int), GFP_KERNEL);
- if (NULL == fixed->regs)
- goto err_fixed_regs_alloc;
- fixed->regs_num = MII_REGS_NUM;
- fixed->phy_status.speed = speed;
- fixed->phy_status.duplex = duplex;
- fixed->phy_status.link = 1;
- 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;
- new_bus->dev = dev;
- dev_set_drvdata(dev, new_bus);
- /* 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;
- /*
- * 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...
- */
- fixed->phydev = phydev;
- phydev->speed = speed;
- phydev->duplex = duplex;
- phydev->irq = PHY_IGNORE_INTERRUPT;
- phydev->dev.bus = &mdio_bus_type;
- snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
- PHY_ID_FMT, bus_id, phy_id);
- phydev->bus = new_bus;
- 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;
- }
- //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_DESCRIPTION("Fixed PHY device & driver for PAL");
- 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);
|