[RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
Francois Romieu
romieu at fr.zoreil.com
Tue Jul 26 07:06:46 EST 2005
Andy Fleming <afleming at freescale.com> :
> This patch contains the PHY layer itself, no phy drivers
[...]
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/Kconfig
[...]
> +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
[snip]
They try to escape...
[...]
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/Makefile
> @@ -0,0 +1,9 @@
> +# 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
...and they are not alone. :o)
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -0,0 +1,175 @@
[...]
> +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)
Be spartan:
if (!bus || !bus->name || !bus->read || !bus->write)
> + return -EINVAL;
> +
> + if (bus->reset)
> + bus->reset(bus);
> +
> + for (i=0; i < PHY_MAX_ADDR; i++) {
for (i = 0; ...
> + struct phy_device *phydev;
> +
> + phydev = get_phy_device(bus, i);
> +
> + /* 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;
> +
> + phydev->bus = bus;
> +
> + sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id,
Imho you are going a bit too far with empty lines. Not a real issue.
> i);
Something decided to wrap the lines after you did the patch. It appears
several times in the patch (+ extra tabs/spaces at the end of the lines:
there are plenty of nice colorised editors around).
> +
> + err = device_register(&phydev->dev);
> +
> + if (err)
> + printk("phy %d did not register (%d)\n",
> + i, err);
Missing KERN_SOMETHING in the printk.
> +
> + /* If get_phy_device returned NULL, it may be
> + * because an error occurred. If so, we return
> + * that error */
> + } else if (errno)
> + return errno;
I'd rather use ERR_PTR/PTR_ERR/IS_ERR + goto in the first place
(then the previous printk will fit on a single line).
> +
> + 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++)
for( i = 0, ... + missing brace.
[...]
> +static int mdio_bus_suspend(struct device * dev, u32 state)
> +{
> + int ret = 0;
> +
> + if (dev->driver && dev->driver->suspend) {
> + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
> + if (ret == 0)
> + ret = dev->driver->suspend(dev, state,
> SUSPEND_SAVE_STATE);
> + if (ret == 0)
> + ret = dev->driver->suspend(dev, state,
> SUSPEND_POWER_DOWN);
> + }
Copy/paste abuse:
struct device_driver *drv = dev->driver;
(appears in several functions)
[...]
> +struct bus_type mdio_bus_type = {
> + .name = "mdio_bus",
> + .match = mdio_bus_match,
> + .suspend= mdio_bus_suspend,
^^
[...]
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/phy.c
[...]
> +int phy_read(struct phy_device *phydev, u16 regnum);
> +int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
> +void phy_change(void *data);
> +void phy_timer(unsigned long data);
phy_read and phy_write do not need to be forward-declared.
Not sure if it is possible for phy_{change/timer} as well.
> +
> +/* Convenience function to print out the current phy status
> + */
> +void phy_print_status(struct phy_device *phydev)
> +{
> + pr_info("%s: Link is %s", phydev->dev.bus_id,
> + phydev->link ? "Up" : "Down");
> + if (phydev->link)
> + printk(" - %d/%s", phydev->speed,
Missing KERN_SOMETHING in the printk.
[...]
> +static inline int phy_aneg_done(struct phy_device *phydev)
> +{
> + int retval;
> +
> + retval = phy_read(phydev, MII_BMSR);
> +
> + if (retval < 0)
> + return retval;
> +
> + return retval & BMSR_ANEGCOMPLETE;
Please use a ternary operator.
> +}
> +
> +/* phy_start_aneg
> + *
> + * description: Calls the PHY driver's config_aneg, and then
> + * sets the PHY state to PHY_AN if auto-negotiation is enabled,
> + * and to PHY_FORCING if auto-negotiation is disabled. Unless
> + * the PHY is currently HALTED.
> + */
> +int phy_start_aneg(struct phy_device *phydev)
> +{
> + int err = 0;
Unneeded initialization.
> +
> + spin_lock(&phydev->lock);
> +
> + if (AUTONEG_DISABLE == phydev->autoneg)
> + phy_sanitize_settings(phydev);
> +
> + err = phydev->drv->config_aneg(phydev);
> +
> + if (err < 0)
> + return err;
The lock should be released. Add a 'goto out_unlock;' ?
> +
> + if (phydev->state != PHY_HALTED) {
> + if (AUTONEG_ENABLE == phydev->autoneg) {
> + phydev->state = PHY_AN;
> + phydev->link_timeout = PHY_AN_TIMEOUT;
> + } else {
> + phydev->state = PHY_FORCING;
> + phydev->link_timeout = PHY_FORCE_TIMEOUT;
> + }
> + }
> +
out_unlock:
> + spin_unlock(&phydev->lock);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(phy_start_aneg);
> +
> +
[...]
> +/* A mapping of all SUPPORTED settings to speed/duplex */
> +static struct phy_setting settings[] = {
> + { .speed = 10000, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_10000baseT_Full,
> + },
> + { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_1000baseT_Full,
> + },
> + { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_1000baseT_Half,
> + },
> + { .speed = SPEED_100, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_100baseT_Full,
> + },
> + { .speed = SPEED_100, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_100baseT_Half,
> + },
> + { .speed = SPEED_10, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_10baseT_Full,
> + },
> + { .speed = SPEED_10, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_10baseT_Half,
> + },
> +};
Would you veto some macro to initialise this array ?
> +
> +#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
The kernel provides ARRAY_SIZE
[...]
> +static inline int phy_find_setting(int speed, int duplex)
> +{
> + int idx = 0;
> +
> + while (idx < MAX_NUM_SETTINGS &&
> + (settings[idx].speed != speed ||
> + settings[idx].duplex != duplex))
> + idx++;
"for" loop in disguise ?
> +
> + return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
Ok (dunno if "idx % MAX_NUM_SETTINGS" is more idiomatic or not).
[...]
> +int phy_start_interrupts(struct phy_device *phydev)
> +{
> + int err = 0;
> +
> + INIT_WORK(&phydev->phy_queue, phy_change, phydev);
> +
> + if (request_irq(phydev->irq, phy_interrupt,
> + SA_SHIRQ,
> + "phy_interrupt",
> + phydev) < 0) {
Please, don't do that :o(
err = request_irq(phydev->irq, phy_interrupt, SA_SHIRQ,
"phy_interrupt", phydev);
if (err < 0)
...
> + printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
> + phydev->bus->name,
> + phydev->irq);
> + phydev->irq = PHY_POLL;
> + return 0;
The description of the function says "Returns 0 on success".
[...]
> +/* Bring down the PHY link, and stop checking the status. */
> +void phy_stop(struct phy_device *phydev)
> +{
> + spin_lock(&phydev->lock);
> +
> + if (PHY_HALTED == phydev->state) {
> + spin_unlock(&phydev->lock);
> + return;
> + }
"goto out_unlock;"
> +
> + if (phydev->irq != PHY_POLL) {
> + /* Clear any pending interrupts */
> + phy_clear_interrupt(phydev);
> +
> + /* Disable PHY Interrupts */
> + phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
> + }
> +
> + phydev->state = PHY_HALTED;
> +
> + spin_unlock(&phydev->lock);
> +}
[...]
> +struct phy_device * get_phy_device(struct mii_bus *bus, uint addr)
> +{
uint ?
> + int phy_reg;
> + u32 phy_id;
> + struct phy_device *dev = NULL;
> +
> + errno = 0;
A bit ugly.
> +
> + /* Grab the bits from PHYIR1, and put them
> + * in the upper half */
> + phy_reg = bus->read(bus, addr, MII_PHYSID1);
> +
> + if (phy_reg < 0) {
> + errno = phy_reg;
> + return NULL;
dev = ERR_PTR(phy_reg);
goto out;
...
> + }
> +
> + phy_id = (phy_reg & 0xffff) << 16;
> +
> + /* Grab the bits from PHYIR2, and put them in the lower half */
> + phy_reg = bus->read(bus, addr, MII_PHYSID2);
> +
> + if (phy_reg < 0) {
> + errno = phy_reg;
> + return NULL;
> + }
> +
> + phy_id |= (phy_reg & 0xffff);
> +
> + /* If the phy_id is all Fs, there is no device there */
> + if (0xffffffff == phy_id)
> + return NULL;
> +
> + /* Otherwise, we allocate the device, and initialize the
> + * default values */
> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
> +
> + if (NULL == dev) {
> + errno = -ENOMEM;
> + return NULL;
> + }
> +
> + memset(dev, 0, sizeof(*dev));
The kernel provides kcalloc.
[...]
> +static int phy_compare_id(struct device *dev, void *data)
> +{
> + const char *name = data;
> +
> + if (strcmp(name, dev->bus_id) == 0)
> + return 1;
> + return 0;
Ternary operator ?
> +}
> +
> +struct phy_device *phy_attach(struct net_device *dev,
> + const char *phy_id, u32 flags)
> +{
> + struct phy_device *phydev = NULL;
Useless initialization.
> + struct bus_type *bus = &mdio_bus_type;
> + struct device *d;
> +
> + /* Search the list of PHY devices on the mdio bus for the
> + * PHY with the requested name */
> + d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
> +
> + if (d) {
> + phydev = to_phy_device(d);
> + } else {
> + printk(KERN_ERR "%s not found\n", phy_id);
> + errno = -ENODEV;
> + return NULL;
> + }
> +
> + /* Assume that if there is no driver, that it doesn't
> + * exist, and we should use the genphy driver. */
> + if (NULL == phydev->dev.driver) {
> + int err;
> + down_write(&phydev->dev.bus->subsys.rwsem);
> + phydev->dev.driver = &genphy_driver.driver;
> +
> + err = phydev->dev.driver->probe(&phydev->dev);
Would it be possible to s/phydev->dev/d/ ?
--
Ueimor
More information about the Linuxppc-embedded
mailing list