AM335X+AR8031网卡驱动(二)
mdiobus_scan()函数中调用的两个关键函数,完成设备驱动的注册
/**
-
get_phy_device - reads the specified PHY device and returns its @phy_device struct
-
@bus: the target MII bus
-
@addr: PHY address on the MII bus
-
Description: Reads the ID registers of the PHY at @addr on the
-
@bus, then allocates and returns the phy_device to represent it.
*/
struct phy_device * get_phy_device(struct mii_bus bus, int addr)
{
struct phy_device dev = NULL;
u32 phy_id;
int r;
r = get_phy_id(bus, addr, &phy_id);
if ®
return ERR_PTR®;
/ If the phy_id is mostly Fs, there is no device there /
if ((phy_id & 0x1fffffff) == 0x1fffffff)
return NULL;
dev = phy_device_create(bus, addr, phy_id);
return dev;
}
此函数主要是获取phy_device设备,紧接着调用phy_device_register()函数注册设备。
/ -
phy_device_register - Register the phy device on the MDIO bus
-
@phydev: phy_device structure to be added to the MDIO bus
*/
int phy_device_register(struct phy_device *phydev)
{
int err;/* Don’t register a phy if one is already registered at this
- address */
if (phydev->bus->phy_map[phydev->addr])
return -EINVAL;
phydev->bus->phy_map[phydev->addr] = phydev;
/* Run all of the fixups for this PHY */
phy_scan_fixups(phydev);err = device_register(&phydev->dev);
if (err) {
pr_err(“phy %d failed to register\n”, phydev->addr);
goto out;
}return 0;
- address */
out:
phydev->bus->phy_map[phydev->addr] = NULL;
return err;
}
ti公司的mdio平台总线驱动如下
static int __init davinci_mdio_init(void)
{
return platform_driver_register(&davinci_mdio_driver);
}
platform_driver 结构体中的操作集合
static struct platform_driver davinci_mdio_driver = {
.driver = {
.name = “davinci_mdio”,
.owner = THIS_MODULE,
.pm = &davinci_mdio_pm_ops,
},
.probe = davinci_mdio_probe,
.remove = __devexit_p(davinci_mdio_remove),
};
由davinci_mdio_probe函数完成平台总线驱动的调用
static int __devinit davinci_mdio_probe(struct platform_device *pdev)
{
struct mdio_platform_data *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct davinci_mdio_data *data;
struct resource *res;
struct phy_device *phy;
int ret, addr;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(dev, “failed to alloc device data\n”);
return -ENOMEM;
}
data->pdata = pdata ? (pdata) : default_pdata;
data->bus = mdiobus_alloc();
if (!data->bus) {
dev_err(dev, “failed to alloc mii bus\n”);
ret = -ENOMEM;
goto bail_out;
}
data->bus->name = dev_name(dev);
data->bus->read = davinci_mdio_read,//读操作
data->bus->write = davinci_mdio_write,//写操作
data->bus->reset = davinci_mdio_reset,//复位操作
data->bus->parent = dev;
data->bus->priv = data;
snprintf(data->bus->id, MII_BUS_ID_SIZE, “%x”, pdev->id);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
data->clk = clk_get(&pdev->dev, “fck”);
if (IS_ERR(data->clk)) {
data->clk = NULL;
dev_err(dev, “failed to get device clock\n”);
ret = PTR_ERR(data->clk);
goto bail_out;
}
dev_set_drvdata(dev, data);
data->dev = dev;
spin_lock_init(&data->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, “could not find register map resource\n”);
ret = -ENOENT;
goto bail_out;
}
res = devm_request_mem_region(dev, res->start, resource_size(res),
dev_name(dev));
if (!res) {
dev_err(dev, “could not allocate register map resource\n”);
ret = -ENXIO;
goto bail_out;
}
data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
if (!data->regs) {
dev_err(dev, “could not map mdio registers\n”);
ret = -ENOMEM;
goto bail_out;
}
/ register the mii bus /
ret = mdiobus_register(data->bus);//注册驱动
if (ret)
goto bail_out;
/ scan and dump the bus */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
phy = data->bus->phy_map[addr];
if (phy) {
dev_info(dev, “phy[%d]: device %s, driver %s\n”,
phy->addr, dev_name(&phy->dev),
phy->drv ? phy->drv->name : “unknown”);
}
}
return 0;
bail_out:
if (data->bus)
mdiobus_free(data->bus);
if (data->clk)
clk_put(data->clk);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
kfree(data);
return ret;
}
接下来将会继续对phy芯片驱动做进一步分析