phylib: Support phy module autoloading
We don't use the normal hotplug mechanism because it doesn't work. It will load the module some time after the device appears, but that's not good enough for us -- we need the driver loaded _immediately_ because otherwise the NIC driver may just abort and then the phy 'device' goes away. [bwh: s/phy/mdio/ in module alias, kerneldoc for struct mdio_device_id] Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Signed-off-by: Ben Hutchings <ben@decadent.org.uk> Acked-by: Andy Fleming <afleming@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
9092c658ba
commit
8626d3b432
|
@ -149,6 +149,7 @@ EXPORT_SYMBOL(phy_scan_fixups);
|
|||
struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
|
||||
{
|
||||
struct phy_device *dev;
|
||||
|
||||
/* We allocate the device, and initialize the
|
||||
* default values */
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
|
@ -179,6 +180,17 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
|
|||
mutex_init(&dev->lock);
|
||||
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
|
||||
|
||||
/* Request the appropriate module unconditionally; don't
|
||||
bother trying to do so only if it isn't already loaded,
|
||||
because that gets complicated. A hotplug event would have
|
||||
done an unconditional modprobe anyway.
|
||||
We don't do normal hotplug because it won't work for MDIO
|
||||
-- because it relies on the device staying around for long
|
||||
enough for the driver to get loaded. With MDIO, the NIC
|
||||
driver will get bored and give up as soon as it finds that
|
||||
there's no driver _already_ loaded. */
|
||||
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_device_create);
|
||||
|
|
|
@ -474,4 +474,30 @@ struct platform_device_id {
|
|||
__attribute__((aligned(sizeof(kernel_ulong_t))));
|
||||
};
|
||||
|
||||
#define MDIO_MODULE_PREFIX "mdio:"
|
||||
|
||||
#define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d"
|
||||
#define MDIO_ID_ARGS(_id) \
|
||||
(_id)>>31, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \
|
||||
((_id)>>27) & 1, ((_id)>>26) & 1, ((_id)>>25) & 1, ((_id)>>24) & 1, \
|
||||
((_id)>>23) & 1, ((_id)>>22) & 1, ((_id)>>21) & 1, ((_id)>>20) & 1, \
|
||||
((_id)>>19) & 1, ((_id)>>18) & 1, ((_id)>>17) & 1, ((_id)>>16) & 1, \
|
||||
((_id)>>15) & 1, ((_id)>>14) & 1, ((_id)>>13) & 1, ((_id)>>12) & 1, \
|
||||
((_id)>>11) & 1, ((_id)>>10) & 1, ((_id)>>9) & 1, ((_id)>>8) & 1, \
|
||||
((_id)>>7) & 1, ((_id)>>6) & 1, ((_id)>>5) & 1, ((_id)>>4) & 1, \
|
||||
((_id)>>3) & 1, ((_id)>>2) & 1, ((_id)>>1) & 1, (_id) & 1
|
||||
|
||||
/**
|
||||
* struct mdio_device_id - identifies PHY devices on an MDIO/MII bus
|
||||
* @phy_id: The result of
|
||||
* (mdio_read(&MII_PHYSID1) << 16 | mdio_read(&PHYSID2)) & @phy_id_mask
|
||||
* for this PHY type
|
||||
* @phy_id_mask: Defines the significant bits of @phy_id. A value of 0
|
||||
* is used to terminate an array of struct mdio_device_id.
|
||||
*/
|
||||
struct mdio_device_id {
|
||||
__u32 phy_id;
|
||||
__u32 phy_id_mask;
|
||||
};
|
||||
|
||||
#endif /* LINUX_MOD_DEVICETABLE_H */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/mii.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
||||
|
|
|
@ -796,6 +796,28 @@ static int do_platform_entry(const char *filename,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int do_mdio_entry(const char *filename,
|
||||
struct mdio_device_id *id, char *alias)
|
||||
{
|
||||
int i;
|
||||
|
||||
alias += sprintf(alias, MDIO_MODULE_PREFIX);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!((id->phy_id_mask >> (31-i)) & 1))
|
||||
*(alias++) = '?';
|
||||
else if ((id->phy_id >> (31-i)) & 1)
|
||||
*(alias++) = '1';
|
||||
else
|
||||
*(alias++) = '0';
|
||||
}
|
||||
|
||||
/* Terminate the string */
|
||||
*alias = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ignore any prefix, eg. some architectures prepend _ */
|
||||
static inline int sym_is(const char *symbol, const char *name)
|
||||
{
|
||||
|
@ -943,6 +965,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
|
|||
do_table(symval, sym->st_size,
|
||||
sizeof(struct platform_device_id), "platform",
|
||||
do_platform_entry, mod);
|
||||
else if (sym_is(symname, "__mod_mdio_device_table"))
|
||||
do_table(symval, sym->st_size,
|
||||
sizeof(struct mdio_device_id), "mdio",
|
||||
do_mdio_entry, mod);
|
||||
free(zeros);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue