linux/drivers/net/phy/fixed_phy.c
<<
>>
Prefs
   1/*
   2 * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
   3 *
   4 * Author: Vitaly Bordug <vbordug@ru.mvista.com>
   5 *         Anton Vorontsov <avorontsov@ru.mvista.com>
   6 *
   7 * Copyright (c) 2006-2007 MontaVista Software, Inc.
   8 *
   9 * This program is free software; you can redistribute  it and/or modify it
  10 * under  the terms of  the GNU General  Public License as published by the
  11 * Free Software Foundation;  either version 2 of the  License, or (at your
  12 * option) any later version.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18#include <linux/list.h>
  19#include <linux/mii.h>
  20#include <linux/phy.h>
  21#include <linux/phy_fixed.h>
  22#include <linux/err.h>
  23#include <linux/slab.h>
  24#include <linux/of.h>
  25
  26#define MII_REGS_NUM 29
  27
  28struct fixed_mdio_bus {
  29        int irqs[PHY_MAX_ADDR];
  30        struct mii_bus *mii_bus;
  31        struct list_head phys;
  32};
  33
  34struct fixed_phy {
  35        int addr;
  36        u16 regs[MII_REGS_NUM];
  37        struct phy_device *phydev;
  38        struct fixed_phy_status status;
  39        int (*link_update)(struct net_device *, struct fixed_phy_status *);
  40        struct list_head node;
  41};
  42
  43static struct platform_device *pdev;
  44static struct fixed_mdio_bus platform_fmb = {
  45        .phys = LIST_HEAD_INIT(platform_fmb.phys),
  46};
  47
  48static int fixed_phy_update_regs(struct fixed_phy *fp)
  49{
  50        u16 bmsr = BMSR_ANEGCAPABLE;
  51        u16 bmcr = 0;
  52        u16 lpagb = 0;
  53        u16 lpa = 0;
  54
  55        if (fp->status.duplex) {
  56                bmcr |= BMCR_FULLDPLX;
  57
  58                switch (fp->status.speed) {
  59                case 1000:
  60                        bmsr |= BMSR_ESTATEN;
  61                        bmcr |= BMCR_SPEED1000;
  62                        lpagb |= LPA_1000FULL;
  63                        break;
  64                case 100:
  65                        bmsr |= BMSR_100FULL;
  66                        bmcr |= BMCR_SPEED100;
  67                        lpa |= LPA_100FULL;
  68                        break;
  69                case 10:
  70                        bmsr |= BMSR_10FULL;
  71                        lpa |= LPA_10FULL;
  72                        break;
  73                default:
  74                        pr_warn("fixed phy: unknown speed\n");
  75                        return -EINVAL;
  76                }
  77        } else {
  78                switch (fp->status.speed) {
  79                case 1000:
  80                        bmsr |= BMSR_ESTATEN;
  81                        bmcr |= BMCR_SPEED1000;
  82                        lpagb |= LPA_1000HALF;
  83                        break;
  84                case 100:
  85                        bmsr |= BMSR_100HALF;
  86                        bmcr |= BMCR_SPEED100;
  87                        lpa |= LPA_100HALF;
  88                        break;
  89                case 10:
  90                        bmsr |= BMSR_10HALF;
  91                        lpa |= LPA_10HALF;
  92                        break;
  93                default:
  94                        pr_warn("fixed phy: unknown speed\n");
  95                        return -EINVAL;
  96                }
  97        }
  98
  99        if (fp->status.link)
 100                bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
 101
 102        if (fp->status.pause)
 103                lpa |= LPA_PAUSE_CAP;
 104
 105        if (fp->status.asym_pause)
 106                lpa |= LPA_PAUSE_ASYM;
 107
 108        fp->regs[MII_PHYSID1] = 0;
 109        fp->regs[MII_PHYSID2] = 0;
 110
 111        fp->regs[MII_BMSR] = bmsr;
 112        fp->regs[MII_BMCR] = bmcr;
 113        fp->regs[MII_LPA] = lpa;
 114        fp->regs[MII_STAT1000] = lpagb;
 115
 116        return 0;
 117}
 118
 119static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
 120{
 121        struct fixed_mdio_bus *fmb = bus->priv;
 122        struct fixed_phy *fp;
 123
 124        if (reg_num >= MII_REGS_NUM)
 125                return -1;
 126
 127        /* We do not support emulating Clause 45 over Clause 22 register reads
 128         * return an error instead of bogus data.
 129         */
 130        switch (reg_num) {
 131        case MII_MMD_CTRL:
 132        case MII_MMD_DATA:
 133                return -1;
 134        default:
 135                break;
 136        }
 137
 138        list_for_each_entry(fp, &fmb->phys, node) {
 139                if (fp->addr == phy_addr) {
 140                        /* Issue callback if user registered it. */
 141                        if (fp->link_update) {
 142                                fp->link_update(fp->phydev->attached_dev,
 143                                                &fp->status);
 144                                fixed_phy_update_regs(fp);
 145                        }
 146                        return fp->regs[reg_num];
 147                }
 148        }
 149
 150        return 0xFFFF;
 151}
 152
 153static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
 154                            u16 val)
 155{
 156        return 0;
 157}
 158
 159/*
 160 * If something weird is required to be done with link/speed,
 161 * network driver is able to assign a function to implement this.
 162 * May be useful for PHY's that need to be software-driven.
 163 */
 164int fixed_phy_set_link_update(struct phy_device *phydev,
 165                              int (*link_update)(struct net_device *,
 166                                                 struct fixed_phy_status *))
 167{
 168        struct fixed_mdio_bus *fmb = &platform_fmb;
 169        struct fixed_phy *fp;
 170
 171        if (!phydev || !phydev->bus)
 172                return -EINVAL;
 173
 174        list_for_each_entry(fp, &fmb->phys, node) {
 175                if (fp->addr == phydev->addr) {
 176                        fp->link_update = link_update;
 177                        fp->phydev = phydev;
 178                        return 0;
 179                }
 180        }
 181
 182        return -ENOENT;
 183}
 184EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
 185
 186int fixed_phy_update_state(struct phy_device *phydev,
 187                           const struct fixed_phy_status *status,
 188                           const struct fixed_phy_status *changed)
 189{
 190        struct fixed_mdio_bus *fmb = &platform_fmb;
 191        struct fixed_phy *fp;
 192
 193        if (!phydev || !phydev->bus)
 194                return -EINVAL;
 195
 196        list_for_each_entry(fp, &fmb->phys, node) {
 197                if (fp->addr == phydev->addr) {
 198#define _UPD(x) if (changed->x) \
 199        fp->status.x = status->x
 200                        _UPD(link);
 201                        _UPD(speed);
 202                        _UPD(duplex);
 203                        _UPD(pause);
 204                        _UPD(asym_pause);
 205#undef _UPD
 206                        fixed_phy_update_regs(fp);
 207                        return 0;
 208                }
 209        }
 210
 211        return -ENOENT;
 212}
 213EXPORT_SYMBOL(fixed_phy_update_state);
 214
 215int fixed_phy_add(unsigned int irq, int phy_addr,
 216                  struct fixed_phy_status *status)
 217{
 218        int ret;
 219        struct fixed_mdio_bus *fmb = &platform_fmb;
 220        struct fixed_phy *fp;
 221
 222        fp = kzalloc(sizeof(*fp), GFP_KERNEL);
 223        if (!fp)
 224                return -ENOMEM;
 225
 226        memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
 227
 228        fmb->irqs[phy_addr] = irq;
 229
 230        fp->addr = phy_addr;
 231        fp->status = *status;
 232
 233        ret = fixed_phy_update_regs(fp);
 234        if (ret)
 235                goto err_regs;
 236
 237        list_add_tail(&fp->node, &fmb->phys);
 238
 239        return 0;
 240
 241err_regs:
 242        kfree(fp);
 243        return ret;
 244}
 245EXPORT_SYMBOL_GPL(fixed_phy_add);
 246
 247void fixed_phy_del(int phy_addr)
 248{
 249        struct fixed_mdio_bus *fmb = &platform_fmb;
 250        struct fixed_phy *fp, *tmp;
 251
 252        list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
 253                if (fp->addr == phy_addr) {
 254                        list_del(&fp->node);
 255                        kfree(fp);
 256                        return;
 257                }
 258        }
 259}
 260EXPORT_SYMBOL_GPL(fixed_phy_del);
 261
 262static int phy_fixed_addr;
 263static DEFINE_SPINLOCK(phy_fixed_addr_lock);
 264
 265struct phy_device *fixed_phy_register(unsigned int irq,
 266                                      struct fixed_phy_status *status,
 267                                      struct device_node *np)
 268{
 269        struct fixed_mdio_bus *fmb = &platform_fmb;
 270        struct phy_device *phy;
 271        int phy_addr;
 272        int ret;
 273
 274        /* Get the next available PHY address, up to PHY_MAX_ADDR */
 275        spin_lock(&phy_fixed_addr_lock);
 276        if (phy_fixed_addr == PHY_MAX_ADDR) {
 277                spin_unlock(&phy_fixed_addr_lock);
 278                return ERR_PTR(-ENOSPC);
 279        }
 280        phy_addr = phy_fixed_addr++;
 281        spin_unlock(&phy_fixed_addr_lock);
 282
 283        ret = fixed_phy_add(PHY_POLL, phy_addr, status);
 284        if (ret < 0)
 285                return ERR_PTR(ret);
 286
 287        phy = get_phy_device(fmb->mii_bus, phy_addr, false);
 288        if (!phy || IS_ERR(phy)) {
 289                fixed_phy_del(phy_addr);
 290                return ERR_PTR(-EINVAL);
 291        }
 292
 293        of_node_get(np);
 294        phy->dev.of_node = np;
 295
 296        ret = phy_device_register(phy);
 297        if (ret) {
 298                phy_device_free(phy);
 299                of_node_put(np);
 300                fixed_phy_del(phy_addr);
 301                return ERR_PTR(ret);
 302        }
 303
 304        return phy;
 305}
 306EXPORT_SYMBOL_GPL(fixed_phy_register);
 307
 308static int __init fixed_mdio_bus_init(void)
 309{
 310        struct fixed_mdio_bus *fmb = &platform_fmb;
 311        int ret;
 312
 313        pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
 314        if (IS_ERR(pdev)) {
 315                ret = PTR_ERR(pdev);
 316                goto err_pdev;
 317        }
 318
 319        fmb->mii_bus = mdiobus_alloc();
 320        if (fmb->mii_bus == NULL) {
 321                ret = -ENOMEM;
 322                goto err_mdiobus_reg;
 323        }
 324
 325        snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
 326        fmb->mii_bus->name = "Fixed MDIO Bus";
 327        fmb->mii_bus->priv = fmb;
 328        fmb->mii_bus->parent = &pdev->dev;
 329        fmb->mii_bus->read = &fixed_mdio_read;
 330        fmb->mii_bus->write = &fixed_mdio_write;
 331        fmb->mii_bus->irq = fmb->irqs;
 332
 333        ret = mdiobus_register(fmb->mii_bus);
 334        if (ret)
 335                goto err_mdiobus_alloc;
 336
 337        return 0;
 338
 339err_mdiobus_alloc:
 340        mdiobus_free(fmb->mii_bus);
 341err_mdiobus_reg:
 342        platform_device_unregister(pdev);
 343err_pdev:
 344        return ret;
 345}
 346module_init(fixed_mdio_bus_init);
 347
 348static void __exit fixed_mdio_bus_exit(void)
 349{
 350        struct fixed_mdio_bus *fmb = &platform_fmb;
 351        struct fixed_phy *fp, *tmp;
 352
 353        mdiobus_unregister(fmb->mii_bus);
 354        mdiobus_free(fmb->mii_bus);
 355        platform_device_unregister(pdev);
 356
 357        list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
 358                list_del(&fp->node);
 359                kfree(fp);
 360        }
 361}
 362module_exit(fixed_mdio_bus_exit);
 363
 364MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
 365MODULE_AUTHOR("Vitaly Bordug");
 366MODULE_LICENSE("GPL");
 367