Commit 11b0bacd717c285c94dbb56505a28434b34f0639

Authored by Vitaly Bordug
Committed by Jeff Garzik
1 parent ec42cdb624

[PATCH] PAL: Support of the fixed PHY

This makes it possible for HW PHY-less boards to utilize PAL goodies.  Generic
routines to connect to fixed PHY are provided, as well as ability to specify
software callback that fills up link, speed, etc.  information into PHY
descriptor (the latter feature not tested so far).

Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

Showing 6 changed files with 408 additions and 21 deletions Side-by-side Diff

drivers/net/phy/Kconfig
... ... @@ -56,5 +56,22 @@
56 56 ---help---
57 57 Currently supports the LAN83C185 PHY
58 58  
  59 +config FIXED_PHY
  60 + tristate "Drivers for PHY emulation on fixed speed/link"
  61 + depends on PHYLIB
  62 + ---help---
  63 + Adds the driver to PHY layer to cover the boards that do not have any PHY bound,
  64 + but with the ability to manipulate with speed/link in software. The relavant MII
  65 + speed/duplex parameters could be effectively handled in user-specified fuction.
  66 + Currently tested with mpc866ads.
  67 +
  68 +config FIXED_MII_10_FDX
  69 + bool "Emulation for 10M Fdx fixed PHY behavior"
  70 + depends on FIXED_PHY
  71 +
  72 +config FIXED_MII_100_FDX
  73 + bool "Emulation for 100M Fdx fixed PHY behavior"
  74 + depends on FIXED_PHY
  75 +
59 76 endmenu
drivers/net/phy/Makefile
... ... @@ -10,4 +10,5 @@
10 10 obj-$(CONFIG_QSEMI_PHY) += qsemi.o
11 11 obj-$(CONFIG_SMSC_PHY) += smsc.o
12 12 obj-$(CONFIG_VITESSE_PHY) += vitesse.o
  13 +obj-$(CONFIG_FIXED_PHY) += fixed.o
drivers/net/phy/fixed.c
  1 +/*
  2 + * drivers/net/phy/fixed.c
  3 + *
  4 + * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode.
  5 + *
  6 + * Author: Vitaly Bordug
  7 + *
  8 + * Copyright (c) 2006 MontaVista Software, 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/config.h>
  17 +#include <linux/kernel.h>
  18 +#include <linux/sched.h>
  19 +#include <linux/string.h>
  20 +#include <linux/errno.h>
  21 +#include <linux/unistd.h>
  22 +#include <linux/slab.h>
  23 +#include <linux/interrupt.h>
  24 +#include <linux/init.h>
  25 +#include <linux/delay.h>
  26 +#include <linux/netdevice.h>
  27 +#include <linux/etherdevice.h>
  28 +#include <linux/skbuff.h>
  29 +#include <linux/spinlock.h>
  30 +#include <linux/mm.h>
  31 +#include <linux/module.h>
  32 +#include <linux/mii.h>
  33 +#include <linux/ethtool.h>
  34 +#include <linux/phy.h>
  35 +
  36 +#include <asm/io.h>
  37 +#include <asm/irq.h>
  38 +#include <asm/uaccess.h>
  39 +
  40 +#define MII_REGS_NUM 7
  41 +
  42 +/*
  43 + The idea is to emulate normal phy behavior by responding with
  44 + pre-defined values to mii BMCR read, so that read_status hook could
  45 + take all the needed info.
  46 +*/
  47 +
  48 +struct fixed_phy_status {
  49 + u8 link;
  50 + u16 speed;
  51 + u8 duplex;
  52 +};
  53 +
  54 +/*-----------------------------------------------------------------------------
  55 + * Private information hoder for mii_bus
  56 + *-----------------------------------------------------------------------------*/
  57 +struct fixed_info {
  58 + u16 *regs;
  59 + u8 regs_num;
  60 + struct fixed_phy_status phy_status;
  61 + struct phy_device *phydev; /* pointer to the container */
  62 + /* link & speed cb */
  63 + int(*link_update)(struct net_device*, struct fixed_phy_status*);
  64 +
  65 +};
  66 +
  67 +/*-----------------------------------------------------------------------------
  68 + * If something weird is required to be done with link/speed,
  69 + * network driver is able to assign a function to implement this.
  70 + * May be useful for PHY's that need to be software-driven.
  71 + *-----------------------------------------------------------------------------*/
  72 +int fixed_mdio_set_link_update(struct phy_device* phydev,
  73 + int(*link_update)(struct net_device*, struct fixed_phy_status*))
  74 +{
  75 + struct fixed_info *fixed;
  76 +
  77 + if(link_update == NULL)
  78 + return -EINVAL;
  79 +
  80 + if(phydev) {
  81 + if(phydev->bus) {
  82 + fixed = phydev->bus->priv;
  83 + fixed->link_update = link_update;
  84 + return 0;
  85 + }
  86 + }
  87 + return -EINVAL;
  88 +}
  89 +EXPORT_SYMBOL(fixed_mdio_set_link_update);
  90 +
  91 +/*-----------------------------------------------------------------------------
  92 + * This is used for updating internal mii regs from the status
  93 + *-----------------------------------------------------------------------------*/
  94 +static int fixed_mdio_update_regs(struct fixed_info *fixed)
  95 +{
  96 + u16 *regs = fixed->regs;
  97 + u16 bmsr = 0;
  98 + u16 bmcr = 0;
  99 +
  100 + if(!regs) {
  101 + printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
  102 + return -EINVAL;
  103 + }
  104 +
  105 + if(fixed->phy_status.link)
  106 + bmsr |= BMSR_LSTATUS;
  107 +
  108 + if(fixed->phy_status.duplex) {
  109 + bmcr |= BMCR_FULLDPLX;
  110 +
  111 + switch ( fixed->phy_status.speed ) {
  112 + case 100:
  113 + bmsr |= BMSR_100FULL;
  114 + bmcr |= BMCR_SPEED100;
  115 + break;
  116 +
  117 + case 10:
  118 + bmsr |= BMSR_10FULL;
  119 + break;
  120 + }
  121 + } else {
  122 + switch ( fixed->phy_status.speed ) {
  123 + case 100:
  124 + bmsr |= BMSR_100HALF;
  125 + bmcr |= BMCR_SPEED100;
  126 + break;
  127 +
  128 + case 10:
  129 + bmsr |= BMSR_100HALF;
  130 + break;
  131 + }
  132 + }
  133 +
  134 + regs[MII_BMCR] = bmcr;
  135 + regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx*/
  136 +
  137 + return 0;
  138 +}
  139 +
  140 +static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
  141 +{
  142 + struct fixed_info *fixed = bus->priv;
  143 +
  144 + /* if user has registered link update callback, use it */
  145 + if(fixed->phydev)
  146 + if(fixed->phydev->attached_dev) {
  147 + if(fixed->link_update) {
  148 + fixed->link_update(fixed->phydev->attached_dev,
  149 + &fixed->phy_status);
  150 + fixed_mdio_update_regs(fixed);
  151 + }
  152 + }
  153 +
  154 + if ((unsigned int)location >= fixed->regs_num)
  155 + return -1;
  156 + return fixed->regs[location];
  157 +}
  158 +
  159 +static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
  160 +{
  161 + /* do nothing for now*/
  162 + return 0;
  163 +}
  164 +
  165 +static int fixed_mii_reset(struct mii_bus *bus)
  166 +{
  167 + /*nothing here - no way/need to reset it*/
  168 + return 0;
  169 +}
  170 +
  171 +static int fixed_config_aneg(struct phy_device *phydev)
  172 +{
  173 + /* :TODO:03/13/2006 09:45:37 PM::
  174 + The full autoneg funcionality can be emulated,
  175 + but no need to have anything here for now
  176 + */
  177 + return 0;
  178 +}
  179 +
  180 +/*-----------------------------------------------------------------------------
  181 + * the manual bind will do the magic - with phy_id_mask == 0
  182 + * match will never return true...
  183 + *-----------------------------------------------------------------------------*/
  184 +static struct phy_driver fixed_mdio_driver = {
  185 + .name = "Fixed PHY",
  186 + .features = PHY_BASIC_FEATURES,
  187 + .config_aneg = fixed_config_aneg,
  188 + .read_status = genphy_read_status,
  189 + .driver = { .owner = THIS_MODULE,},
  190 +};
  191 +
  192 +/*-----------------------------------------------------------------------------
  193 + * This func is used to create all the necessary stuff, bind
  194 + * the fixed phy driver and register all it on the mdio_bus_type.
  195 + * speed is either 10 or 100, duplex is boolean.
  196 + * number is used to create multiple fixed PHYs, so that several devices can
  197 + * utilize them simultaneously.
  198 + *-----------------------------------------------------------------------------*/
  199 +static int fixed_mdio_register_device(int number, int speed, int duplex)
  200 +{
  201 + struct mii_bus *new_bus;
  202 + struct fixed_info *fixed;
  203 + struct phy_device *phydev;
  204 + int err = 0;
  205 +
  206 + struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
  207 +
  208 + if (NULL == dev)
  209 + return -ENOMEM;
  210 +
  211 + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
  212 +
  213 + if (NULL == new_bus) {
  214 + kfree(dev);
  215 + return -ENOMEM;
  216 + }
  217 + fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
  218 +
  219 + if (NULL == fixed) {
  220 + kfree(dev);
  221 + kfree(new_bus);
  222 + return -ENOMEM;
  223 + }
  224 +
  225 + fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
  226 + fixed->regs_num = MII_REGS_NUM;
  227 + fixed->phy_status.speed = speed;
  228 + fixed->phy_status.duplex = duplex;
  229 + fixed->phy_status.link = 1;
  230 +
  231 + new_bus->name = "Fixed MII Bus",
  232 + new_bus->read = &fixed_mii_read,
  233 + new_bus->write = &fixed_mii_write,
  234 + new_bus->reset = &fixed_mii_reset,
  235 +
  236 + /*set up workspace*/
  237 + fixed_mdio_update_regs(fixed);
  238 + new_bus->priv = fixed;
  239 +
  240 + new_bus->dev = dev;
  241 + dev_set_drvdata(dev, new_bus);
  242 +
  243 + /* create phy_device and register it on the mdio bus */
  244 + phydev = phy_device_create(new_bus, 0, 0);
  245 +
  246 + /*
  247 + Put the phydev pointer into the fixed pack so that bus read/write code could
  248 + be able to access for instance attached netdev. Well it doesn't have to do
  249 + so, only in case of utilizing user-specified link-update...
  250 + */
  251 + fixed->phydev = phydev;
  252 +
  253 + if(NULL == phydev) {
  254 + err = -ENOMEM;
  255 + goto device_create_fail;
  256 + }
  257 +
  258 + phydev->irq = -1;
  259 + phydev->dev.bus = &mdio_bus_type;
  260 +
  261 + if(number)
  262 + snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
  263 + "fixed_%d@%d:%d", number, speed, duplex);
  264 + else
  265 + snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
  266 + "fixed@%d:%d", speed, duplex);
  267 + phydev->bus = new_bus;
  268 +
  269 + err = device_register(&phydev->dev);
  270 + if(err) {
  271 + printk(KERN_ERR "Phy %s failed to register\n",
  272 + phydev->dev.bus_id);
  273 + goto bus_register_fail;
  274 + }
  275 +
  276 + /*
  277 + the mdio bus has phy_id match... In order not to do it
  278 + artificially, we are binding the driver here by hand;
  279 + it will be the same for all the fixed phys anyway.
  280 + */
  281 + down_write(&phydev->dev.bus->subsys.rwsem);
  282 +
  283 + phydev->dev.driver = &fixed_mdio_driver.driver;
  284 +
  285 + err = phydev->dev.driver->probe(&phydev->dev);
  286 + if(err < 0) {
  287 + printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
  288 + up_write(&phydev->dev.bus->subsys.rwsem);
  289 + goto probe_fail;
  290 + }
  291 +
  292 + device_bind_driver(&phydev->dev);
  293 + up_write(&phydev->dev.bus->subsys.rwsem);
  294 +
  295 + return 0;
  296 +
  297 +probe_fail:
  298 + device_unregister(&phydev->dev);
  299 +bus_register_fail:
  300 + kfree(phydev);
  301 +device_create_fail:
  302 + kfree(dev);
  303 + kfree(new_bus);
  304 + kfree(fixed);
  305 +
  306 + return err;
  307 +}
  308 +
  309 +
  310 +MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
  311 +MODULE_AUTHOR("Vitaly Bordug");
  312 +MODULE_LICENSE("GPL");
  313 +
  314 +static int __init fixed_init(void)
  315 +{
  316 + int ret;
  317 + int duplex = 0;
  318 +
  319 + /* register on the bus... Not expected to be matched with anything there... */
  320 + phy_driver_register(&fixed_mdio_driver);
  321 +
  322 + /* So let the fun begin...
  323 + We will create several mdio devices here, and will bound the upper
  324 + driver to them.
  325 +
  326 + Then the external software can lookup the phy bus by searching
  327 + fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
  328 + virtual 100M Fdx phy.
  329 +
  330 + In case several virtual PHYs required, the bus_id will be in form
  331 + fixed_<num>@<speed>:<duplex>, which make it able even to define
  332 + driver-specific link control callback, if for instance PHY is completely
  333 + SW-driven.
  334 +
  335 + */
  336 +
  337 +#ifdef CONFIG_FIXED_MII_DUPLEX
  338 + duplex = 1;
  339 +#endif
  340 +
  341 +#ifdef CONFIG_FIXED_MII_100_FDX
  342 + fixed_mdio_register_device(0, 100, 1);
  343 +#endif
  344 +
  345 +#ifdef CONFIX_FIXED_MII_10_FDX
  346 + fixed_mdio_register_device(0, 10, 1);
  347 +#endif
  348 + return 0;
  349 +}
  350 +
  351 +static void __exit fixed_exit(void)
  352 +{
  353 + phy_driver_unregister(&fixed_mdio_driver);
  354 + /* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
  355 +}
  356 +
  357 +module_init(fixed_init);
  358 +module_exit(fixed_exit);
drivers/net/phy/mdio_bus.c
... ... @@ -159,6 +159,7 @@
159 159 .suspend = mdio_bus_suspend,
160 160 .resume = mdio_bus_resume,
161 161 };
  162 +EXPORT_SYMBOL(mdio_bus_type);
162 163  
163 164 int __init mdio_bus_init(void)
164 165 {
drivers/net/phy/phy_device.c
... ... @@ -45,6 +45,35 @@
45 45 extern int mdio_bus_init(void);
46 46 extern void mdio_bus_exit(void);
47 47  
  48 +struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
  49 +{
  50 + struct phy_device *dev;
  51 + /* We allocate the device, and initialize the
  52 + * default values */
  53 + dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
  54 +
  55 + if (NULL == dev)
  56 + return (struct phy_device*) PTR_ERR((void*)-ENOMEM);
  57 +
  58 + dev->speed = 0;
  59 + dev->duplex = -1;
  60 + dev->pause = dev->asym_pause = 0;
  61 + dev->link = 1;
  62 +
  63 + dev->autoneg = AUTONEG_ENABLE;
  64 +
  65 + dev->addr = addr;
  66 + dev->phy_id = phy_id;
  67 + dev->bus = bus;
  68 +
  69 + dev->state = PHY_DOWN;
  70 +
  71 + spin_lock_init(&dev->lock);
  72 +
  73 + return dev;
  74 +}
  75 +EXPORT_SYMBOL(phy_device_create);
  76 +
48 77 /* get_phy_device
49 78 *
50 79 * description: Reads the ID registers of the PHY at addr on the
... ... @@ -78,27 +107,7 @@
78 107 if (0xffffffff == phy_id)
79 108 return NULL;
80 109  
81   - /* Otherwise, we allocate the device, and initialize the
82   - * default values */
83   - dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
84   -
85   - if (NULL == dev)
86   - return ERR_PTR(-ENOMEM);
87   -
88   - dev->speed = 0;
89   - dev->duplex = -1;
90   - dev->pause = dev->asym_pause = 0;
91   - dev->link = 1;
92   -
93   - dev->autoneg = AUTONEG_ENABLE;
94   -
95   - dev->addr = addr;
96   - dev->phy_id = phy_id;
97   - dev->bus = bus;
98   -
99   - dev->state = PHY_DOWN;
100   -
101   - spin_lock_init(&dev->lock);
  110 + dev = phy_device_create(bus, addr, phy_id);
102 111  
103 112 return dev;
104 113 }
... ... @@ -378,6 +378,7 @@
378 378 struct mii_ioctl_data *mii_data, int cmd);
379 379 int phy_start_interrupts(struct phy_device *phydev);
380 380 void phy_print_status(struct phy_device *phydev);
  381 +struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id);
381 382  
382 383 extern struct bus_type mdio_bus_type;
383 384 #endif /* __PHY_H */