2 * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
4 * Author: Vitaly Bordug <vbordug@ru.mvista.com>
5 * Anton Vorontsov <avorontsov@ru.mvista.com>
7 * Copyright (c) 2006-2007 MontaVista Software, Inc.
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.
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>
25 #define MII_REGS_NUM 29
27 struct fixed_mdio_bus
{
28 int irqs
[PHY_MAX_ADDR
];
29 struct mii_bus
*mii_bus
;
30 struct list_head phys
;
35 u16 regs
[MII_REGS_NUM
];
36 struct phy_device
*phydev
;
37 struct fixed_phy_status status
;
38 int (*link_update
)(struct net_device
*, struct fixed_phy_status
*);
39 struct list_head node
;
42 static struct platform_device
*pdev
;
43 static struct fixed_mdio_bus platform_fmb
= {
44 .phys
= LIST_HEAD_INIT(platform_fmb
.phys
),
47 static int fixed_phy_update_regs(struct fixed_phy
*fp
)
49 u16 bmsr
= BMSR_ANEGCAPABLE
;
54 if (fp
->status
.duplex
) {
55 bmcr
|= BMCR_FULLDPLX
;
57 switch (fp
->status
.speed
) {
60 bmcr
|= BMCR_SPEED1000
;
61 lpagb
|= LPA_1000FULL
;
65 bmcr
|= BMCR_SPEED100
;
73 printk(KERN_WARNING
"fixed phy: unknown speed\n");
77 switch (fp
->status
.speed
) {
80 bmcr
|= BMCR_SPEED1000
;
81 lpagb
|= LPA_1000HALF
;
85 bmcr
|= BMCR_SPEED100
;
93 printk(KERN_WARNING
"fixed phy: unknown speed\n");
99 bmsr
|= BMSR_LSTATUS
| BMSR_ANEGCOMPLETE
;
101 if (fp
->status
.pause
)
102 lpa
|= LPA_PAUSE_CAP
;
104 if (fp
->status
.asym_pause
)
105 lpa
|= LPA_PAUSE_ASYM
;
107 fp
->regs
[MII_PHYSID1
] = fp
->id
>> 16;
108 fp
->regs
[MII_PHYSID2
] = fp
->id
;
110 fp
->regs
[MII_BMSR
] = bmsr
;
111 fp
->regs
[MII_BMCR
] = bmcr
;
112 fp
->regs
[MII_LPA
] = lpa
;
113 fp
->regs
[MII_STAT1000
] = lpagb
;
118 static int fixed_mdio_read(struct mii_bus
*bus
, int phy_id
, int reg_num
)
120 struct fixed_mdio_bus
*fmb
= bus
->priv
;
121 struct fixed_phy
*fp
;
123 if (reg_num
>= MII_REGS_NUM
)
126 list_for_each_entry(fp
, &fmb
->phys
, node
) {
127 if (fp
->id
== phy_id
) {
128 /* Issue callback if user registered it. */
129 if (fp
->link_update
) {
130 fp
->link_update(fp
->phydev
->attached_dev
,
132 fixed_phy_update_regs(fp
);
134 return fp
->regs
[reg_num
];
141 static int fixed_mdio_write(struct mii_bus
*bus
, int phy_id
, int reg_num
,
148 * If something weird is required to be done with link/speed,
149 * network driver is able to assign a function to implement this.
150 * May be useful for PHY's that need to be software-driven.
152 int fixed_phy_set_link_update(struct phy_device
*phydev
,
153 int (*link_update
)(struct net_device
*,
154 struct fixed_phy_status
*))
156 struct fixed_mdio_bus
*fmb
= &platform_fmb
;
157 struct fixed_phy
*fp
;
159 if (!link_update
|| !phydev
|| !phydev
->bus
)
162 list_for_each_entry(fp
, &fmb
->phys
, node
) {
163 if (fp
->id
== phydev
->phy_id
) {
164 fp
->link_update
= link_update
;
172 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update
);
174 int fixed_phy_add(unsigned int irq
, int phy_id
,
175 struct fixed_phy_status
*status
)
178 struct fixed_mdio_bus
*fmb
= &platform_fmb
;
179 struct fixed_phy
*fp
;
181 fp
= kzalloc(sizeof(*fp
), GFP_KERNEL
);
185 memset(fp
->regs
, 0xFF, sizeof(fp
->regs
[0]) * MII_REGS_NUM
);
187 fmb
->irqs
[phy_id
] = irq
;
190 fp
->status
= *status
;
192 ret
= fixed_phy_update_regs(fp
);
196 list_add_tail(&fp
->node
, &fmb
->phys
);
204 EXPORT_SYMBOL_GPL(fixed_phy_add
);
206 static int __init
fixed_mdio_bus_init(void)
208 struct fixed_mdio_bus
*fmb
= &platform_fmb
;
211 pdev
= platform_device_register_simple("Fixed MDIO bus", 0, NULL
, 0);
217 fmb
->mii_bus
= mdiobus_alloc();
218 if (fmb
->mii_bus
== NULL
) {
220 goto err_mdiobus_reg
;
223 snprintf(fmb
->mii_bus
->id
, MII_BUS_ID_SIZE
, "fixed-0");
224 fmb
->mii_bus
->name
= "Fixed MDIO Bus";
225 fmb
->mii_bus
->priv
= fmb
;
226 fmb
->mii_bus
->parent
= &pdev
->dev
;
227 fmb
->mii_bus
->read
= &fixed_mdio_read
;
228 fmb
->mii_bus
->write
= &fixed_mdio_write
;
229 fmb
->mii_bus
->irq
= fmb
->irqs
;
231 ret
= mdiobus_register(fmb
->mii_bus
);
233 goto err_mdiobus_alloc
;
238 mdiobus_free(fmb
->mii_bus
);
240 platform_device_unregister(pdev
);
244 module_init(fixed_mdio_bus_init
);
246 static void __exit
fixed_mdio_bus_exit(void)
248 struct fixed_mdio_bus
*fmb
= &platform_fmb
;
249 struct fixed_phy
*fp
, *tmp
;
251 mdiobus_unregister(fmb
->mii_bus
);
252 mdiobus_free(fmb
->mii_bus
);
253 platform_device_unregister(pdev
);
255 list_for_each_entry_safe(fp
, tmp
, &fmb
->phys
, node
) {
260 module_exit(fixed_mdio_bus_exit
);
262 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
263 MODULE_AUTHOR("Vitaly Bordug");
264 MODULE_LICENSE("GPL");