平台总线设备-网络

 

一、引入dts之前

kernel在引入dts前后,platform_device的生成方式是不同的。
在引入设备树之前,platform_device变量是静态定义的。
而引入设备树之后,kernel通过解析设备节点的信息生成platform_device。


 add a platform-level device
 
int platform_device_register(struct platform_device *pdev)
{
        device_initialize(&pdev->dev);
        arch_setup_pdev_archdata(pdev);
        return platform_device_add(pdev);
}
 
 add a platform device to device hierarchy
 This is part 2 of platform_device_register(), though may be called
 separately _iff_ pdev was allocated by platform_device_alloc().
int platform_device_add(struct platform_device *pdev)
{
        int i, ret;
 
        if (!pdev)
                return -EINVAL;
 
        if (!pdev->dev.parent)
                pdev->dev.parent = &platform_bus;
 
        pdev->dev.bus = &platform_bus_type;
 
        switch (pdev->id) {
        default:
                dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
                break;
        case PLATFORM_DEVID_NONE:
                dev_set_name(&pdev->dev, "%s", pdev->name);
                break;
        case PLATFORM_DEVID_AUTO:
                /*
                 * Automatically allocated device ID. We mark it as such so
                 * that we remember it must be freed, and we append a suffix
                 * to avoid namespace collision with explicit IDs.
                 */
                ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
                if (ret < 0)
                        goto err_out;
                pdev->id = ret;
                pdev->id_auto = true;
                dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
                break;
        }
 
        for (i = 0; i < pdev->num_resources; i++) {
                struct resource *p, *r = &pdev->resource[i];
 
                if (r->name == NULL)
                        r->name = dev_name(&pdev->dev);
 
                p = r->parent;
                if (!p) {
                        if (resource_type(r) == IORESOURCE_MEM)
                                p = &iomem_resource;
                        else if (resource_type(r) == IORESOURCE_IO)
                                p = &ioport_resource;
                }
 
                if (p && insert_resource(p, r)) {
                        dev_err(&pdev->dev, "failed to claim resource %d
", i);
                        ret = -EBUSY;
                        goto failed;
                }
        }
 
        pr_debug("Registering platform device '%s'. Parent at %s
",
                 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
        ret = device_add(&pdev->dev);
        if (ret == 0)
                return ret;
 
 failed:
        if (pdev->id_auto) {
                ida_simple_remove(&platform_devid_ida, pdev->id);
                pdev->id = PLATFORM_DEVID_AUTO;
        }
 
        while (--i >= 0) {
                struct resource *r = &pdev->resource[i];
                if (r->parent)
                        release_resource(r);
        }
 
 err_out:
        return ret;
}
 
该函数最终会调用device_add添加设备

二、引入设备树之后


引入设备树之后,主要是通过解析设备树节点生成platform_device。
kernel中有两个阶段会生成platform_device:
第一阶段是在kernel初始化解析设备树时,kernel会为所有包含compatible属性名的第一级node创建platform_device。
如果第一级node的compatible属性值为“simple
-bus”、“simple-mfd”或者"arm,amba-bus",kernel会继续为当前node的包含compatible属性的节点创建platform_device。 一般SOC直接子设备节点对应的platform_device会被生成,而间接子节点对应的platform_device不会生成。 第二阶段是生成第一阶段中未满足match要求的间接子节点对应的platform_device。通常是在父节点设备驱动的probe函数中遍历子节点生成对应的platform_device。 (1)第一阶段 通过调用of_platform_populate解析设备树 //Populate platform_devices from device tree data int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent) { struct device_node *child; int rc = 0; root = root ? of_node_get(root) : of_find_node_by_path("/"); if (!root) return -EINVAL; for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) { of_node_put(child); break; } } of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); return rc; } kernel中有个of_platform_bus_probe函数,该函数和of_platform_populate有点类似,在新的board中最好使 用of_platform_populate来代替of_platform_bus_probe函数。 Create a device for a node and its children. static int of_platform_bus_create(struct device_node *bus, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent, bool strict) { const struct of_dev_auxdata *auxdata; struct device_node *child; struct platform_device *dev; const char *bus_id = NULL; void *platform_data = NULL; int rc = 0; /* Make sure it has a compatible property */ if (strict && (!of_get_property(bus, "compatible", NULL))) { pr_debug("%s() - skipping %s, no compatible prop ", __func__, bus->full_name); return 0; } auxdata = of_dev_lookup(lookup, bus); if (auxdata) { bus_id = auxdata->name; platform_data = auxdata->platform_data; } if (of_device_is_compatible(bus, "arm,primecell")) { /* * Don't return an error here to keep compatibility with older * device tree files. */ of_amba_device_create(bus, bus_id, platform_data, parent); return 0; } dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0; for_each_child_of_node(bus, child) { pr_debug(" create child: %s ", child->full_name); rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); if (rc) { of_node_put(child); break; } } of_node_set_flag(bus, OF_POPULATED_BUS); return rc; } Alloc, initialize and register an of_device static struct platform_device *of_platform_device_create_pdata( struct device_node *np, const char *bus_id, void *platform_data, struct device *parent) { struct platform_device *dev; if (!of_device_is_available(np) || of_node_test_and_set_flag(np, OF_POPULATED)) return NULL; dev = of_device_alloc(np, bus_id, parent); if (!dev) goto err_clear_flag; dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; of_dma_configure(&dev->dev, dev->dev.of_node); of_msi_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0) { of_dma_deconfigure(&dev->dev); platform_device_put(dev); goto err_clear_flag; } return dev; err_clear_flag: of_node_clear_flag(np, OF_POPULATED); return NULL; } of_platform_device_create_pdata函数真正完成了创建platform_device的工作。 调用它的函数主要是做一些准备工作,如查找设备节点、检查兼容性等。 int of_device_add(struct platform_device *ofdev) { BUG_ON(ofdev->dev.of_node == NULL); /* name and id have to be set so that the platform bus doesn't get * confused on matching */ ofdev->name = dev_name(&ofdev->dev); ofdev->id = -1; /* * If this device has not binding numa node in devicetree, that is * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this * device is on the same node as the parent. */ set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node)); return device_add(&ofdev->dev); } (2)第二阶段 在父设备节点的驱动probe函数中调用of_platform_device_create生成子设备节点对应的platforma_device。 struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent) { return of_platform_device_create_pdata(np, bus_id, NULL, parent); } 上述函数实际上调用of_platform_device_create_pdata完成生成platforma_device的工作的。

 fixed rate clock

若想注册一个fixed rate clock, 除了你可以在自己的clock driver里面手动调用clk_register_fixed_rate之外, 还有一种更简单的方式, 那就是用DTS.

你可以在DTS里面用如下方式描述一个fixed rate clock:

    osc32k: osc32k {

    #clock–cells = <0>;

     compatible = “fixed-clock“;

      clock–frequency = <32768>;

               clock-accuracy = xxxx;

               clock-output-names = xxxx;

   };

         关键地方在于compatible一定要是”fixed-clock”

         “drivers/clk/clk-fixed-rate.c”中的of_fixed_clk_setup会负责匹配这个compatible, 这个C文件是clock子系统实现的.

         of_fixed_clk_setup会解析3个参数: clock-frequency, clock-accuracy, clock-output-names

clock-frequency是必须的, 另外2个参数可选.

数解析完毕之后, 会调用clk_register_fixed_rate_with_accuracy向系统注册一个clock.

 

#ifdef CONFIG_OF
static struct clk_hw *_of_fixed_clk_setup(struct device_node *node)
{
        struct clk_hw *hw;
        const char *clk_name = node->name;
        u32 rate;
        u32 accuracy = 0;
        int ret;

        if (of_property_read_u32(node, "clock-frequency", &rate))
                return ERR_PTR(-EIO);

        of_property_read_u32(node, "clock-accuracy", &accuracy);

        of_property_read_string(node, "clock-output-names", &clk_name);

        hw = clk_hw_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
                                                    0, rate, accuracy);
        if (IS_ERR(hw))
                return hw;

        ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
        if (ret) {
                clk_hw_unregister_fixed_rate(hw);
                return ERR_PTR(ret);
        }

        return hw;
}

/**
 * of_fixed_clk_setup() - Setup function for simple fixed rate clock
 * @node:       device node for the clock
 */
void __init of_fixed_clk_setup(struct device_node *node)
{
        _of_fixed_clk_setup(node);
}
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);

static int of_fixed_clk_remove(struct platform_device *pdev)
{
        struct clk_hw *hw = platform_get_drvdata(pdev);

        of_clk_del_provider(pdev->dev.of_node);
        clk_hw_unregister_fixed_rate(hw);

        return 0;
}

static int of_fixed_clk_probe(struct platform_device *pdev)
{
        struct clk_hw *hw;

        /*
         * This function is not executed when of_fixed_clk_setup
         * succeeded.
         */
        hw = _of_fixed_clk_setup(pdev->dev.of_node);
        if (IS_ERR(hw))
                return PTR_ERR(hw);

        platform_set_drvdata(pdev, hw);

        return 0;
}

static const struct of_device_id of_fixed_clk_ids[] = {
        { .compatible = "fixed-clock" },
        { }
};

static struct platform_driver of_fixed_clk_driver = {
        .driver = {
                .name = "of_fixed_clk",
                .of_match_table = of_fixed_clk_ids,
        },
        .probe = of_fixed_clk_probe,
        .remove = of_fixed_clk_remove,
};
builtin_platform_driver(of_fixed_clk_driver);
#endif
static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
        int err;
        struct platform_device *plat_dev;
        struct platform_device_info plat_info;
        struct macb_platform_data plat_data;
        struct resource res[2];

        /* enable pci device */
        err = pcim_enable_device(pdev);
        if (err < 0) {
                dev_err(&pdev->dev, "Enabling PCI device has failed: %d", err);
                return err;
        }

        pci_set_master(pdev);

        /* set up resources */
        memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
        res[0].start = pci_resource_start(pdev, 0);
        res[0].end = pci_resource_end(pdev, 0);
        res[0].name = PCI_DRIVER_NAME;
        res[0].flags = IORESOURCE_MEM;
        res[1].start = pci_irq_vector(pdev, 0);
        res[1].name = PCI_DRIVER_NAME;
        res[1].flags = IORESOURCE_IRQ;

        dev_info(&pdev->dev, "EMAC physical base addr: %pa
",
                 &res[0].start);

        /* set up macb platform data */
        memset(&plat_data, 0, sizeof(plat_data));

        /* initialize clocks */
        plat_data.pclk = clk_register_fixed_rate(&pdev->dev, "pclk", NULL, 0,
                                                 GEM_PCLK_RATE);
        if (IS_ERR(plat_data.pclk)) {
                err = PTR_ERR(plat_data.pclk);
                goto err_pclk_register;
        }

        plat_data.hclk = clk_register_fixed_rate(&pdev->dev, "hclk", NULL, 0,
                                                 GEM_HCLK_RATE);
        if (IS_ERR(plat_data.hclk)) {
                err = PTR_ERR(plat_data.hclk);
                goto err_hclk_register;
        }

设备树加载

dts文件为板级设备描述文件,被编译后为dtb,由bootloader读入,并作为参数传递给linux kernel,入口地址为__fdt_pointer,定义在汇编文件head.S (archarm64kernel)中

linux加载device tree过程如下

setup_machine_fdt()把dtb传入的数据入口地址设置给了initial_boot_params
unflatten_device_tree()根据initial_boot_params创建了device_node树


of_platform_populate()根据device_node树创建了platform_device树
最终:
of_platform_populate()->of_platform_bus_create()->of_platform_device_create_pdata()->of_device_add()->device_add()
并通过device_add()函数实现设备的注册,在device_add()源码的注释中有写明该函数为设备注册的第二步,一个设备若要注册,则该函数或者device_register()函数只允许将其中的一个函数调用1次。
同时,device_add()函数中还调用了bus_probe_device(),对驱动进行匹配探测。
 

platform驱动模型中的硬件资源与驱动关联过程  

设备树中设备的禁用和启用

device tree中的status标识了设备的状态,使用status可以去禁止设备或者启用设备,看下设备树规范中的status可选值

 

 下面是设备树中节点属性status的处理代码,位于内核的drivers/of/base.c中

static bool __of_device_is_available(const struct device_node *device)
{
    const char *status;
    int statlen;

    if (!device)
        return false;

    status = __of_get_property(device, "status", &statlen);
    if (status == NULL)
        return true; //默认为使能

    if (statlen > 0) {
        if (!strcmp(status, "okay") || !strcmp(status, "ok")) //ok和okay都可以
            return true;
    }
    //表中的fail和fail-sss没做具体处理
    return false;
}

/**
 *  of_device_is_available - check if a device is available for use
 *
 *  @device: Node to check for availability
 *
 *  Returns true if the status property is absent or set to "okay" or "ok",
 *  false otherwise
 */
bool of_device_is_available(const struct device_node *device)
{
    unsigned long flags;
    bool res;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    res = __of_device_is_available(device);
    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    return res;

}
EXPORT_SYMBOL(of_device_is_available);
 
#  ls /sys/devices/platform/  
Fixed MDIO bus.0  serial8250        soc               uevent
# 
#  ls /sys/devices/platform/soc/  
1000.rom                        4000.teststatus
1700000.bus-error-unit          a000000.rom
2000000.clint                   driver_override
20000000.serial                 modalias
20010000.spi                    of_node
20020000.timer                  soc:global-external-interrupts
20030000.watchdog               soc:mdio
20040000.i2c                    subsystem
20050000.gpio                   uevent
2010000.cache-controller        waiting_for_supplier
3000.error-device
# 
#  ls /sys/devices/platform/soc/
1000.rom                        4000.teststatus
1700000.bus-error-unit          a000000.rom
2000000.clint                   driver_override
20000000.serial                 modalias
20010000.spi                    of_node
20020000.timer                  soc:global-external-interrupts
20030000.watchdog               soc:mdio
20040000.i2c                    subsystem
20050000.gpio                   uevent
2010000.cache-controller        waiting_for_supplier
3000.error-device
# 
#  ls /sys/devices/platform/soc/soc:mdio/
driver_override       of_node               uevent
modalias              subsystem             waiting_for_supplier
# 
#  ls /sys/devices/platform/soc/soc:mdio/of_node/
compatible      ethernet-phy@1  name            status
# 

ls /sys/bus/platform/devices/

[root@centos7 images]# ls /sys/bus/mdio_bus
devices  drivers  drivers_autoprobe  drivers_probe  uevent
[root@centos7 images]# ls /sys/bus/mdio_bus/devices/
mii-0000:7d:00.0:01  mii-0000:7d:00.1:03  mii-0000:7d:00.2:05  mii-0000:7d:00.3:07
[root@centos7 images]# ls /sys/bus/mdio_bus/drivers
Generic 10G PHY  Generic PHY  RTL8201CP Ethernet  RTL8211B Gigabit Ethernet  RTL8211DN Gigabit Ethernet  RTL8211E Gigabit Ethernet  RTL8211F Gigabit Ethernet
[root@centos7 images]# 

module_platform_driver

对于平台总线设备,可以用两种方法来加载

static int __init xxxx_init(void)
{
    return platform_driver_register(&xxxx_driver);
}
 
static void __exit xxxx_exit(void)
{
    platform_driver_unregister(&xxxx_driver);
}
 
module_init(xxxx_init);
module_exit(xxxx_exit);
 
module_platform_driver(xxxx_driver);

 MARVELL_PHY

 

[root@centos7 linux-5.14]# vim drivers/phy/marvell/
Kconfig                   phy-armada375-usb2.c      phy-berlin-sata.c         phy-mmp3-hsic.c           phy-mvebu-a3700-comphy.c  phy-mvebu-cp110-comphy.c  phy-mvebu-sata.c          phy-pxa-28nm-usb2.c       
Makefile                  phy-armada38x-comphy.c    phy-berlin-usb.c          phy-mmp3-usb.c            phy-mvebu-a3700-utmi.c    phy-mvebu-cp110-utmi.c    phy-pxa-28nm-hsic.c       phy-pxa-usb.c             
[root@centos7 linux-5.14]# grep marvell_hwmon_chip_config -rn *
drivers/net/phy/marvell.c:
[root@centos7 linux-5.14]# cat System.map  | grep marvell_driver
ffffffff812ad7c0 d marvell_drivers
[root@centos7 linux-5.14]# cat System.map  | grep marvell_
ffffffff8046c926 t marvell_get_sset_count
ffffffff8046c93c t marvell_hwmon_read
ffffffff8046c994 t marvell_hwmon_write
ffffffff8046c9c0 t marvell_hwmon_is_visible
ffffffff8046cb5a t marvell_get_stats
ffffffff8046cc7e t marvell_read_page
ffffffff8046ce78 t marvell_write_page
ffffffff8046ce9e t marvell_aneg_done
ffffffff8046d0ce t marvell_config_led
ffffffff8046d144 t marvell_cable_test_start_common
ffffffff8046d1e8 t marvell_vct5_cable_test_tdr_start
ffffffff8046d2c2 t marvell_of_reg_init
ffffffff8046d4ae t marvell_config_init
ffffffff8046d67c t marvell_get_strings
ffffffff8046d6f0 t marvell_handle_interrupt
ffffffff8046d748 t marvell_probe
ffffffff8046d95a t marvell_vct7_cable_test_start
ffffffff8046d9b8 t marvell_config_aneg_fiber
ffffffff8046dba6 t marvell_read_status_page
ffffffff8046dd26 t marvell_read_status
ffffffff8046ddc4 t marvell_config_intr
ffffffff8046dedc t marvell_resume
ffffffff8046df60 t marvell_suspend
ffffffff8046dfe4 t marvell_config_aneg
ffffffff8046e456 t marvell_1011gbe_config_init
ffffffff8046e86a t marvell_vct7_cable_test_get_status
ffffffff8080f6a0 d __initcall__kmod_marvell__390_3147_phy_module_init6
ffffffff80e7f650 d __param_str_marvell_enable
ffffffff80e83428 d marvell_hw_stats
ffffffff80e83458 d marvell_hwmon_chip_info
ffffffff80e83518 d marvell_hwmon_hwmon_ops
ffffffff80e83538 d marvell_hwmon_temp
ffffffff80e83548 d marvell_hwmon_chip
ffffffff80e893f0 d xhci_plat_marvell_armada3700
ffffffff80e89428 d xhci_plat_marvell_armada
ffffffff80fe4a10 r __param_marvell_enable
ffffffff812ad7c0 d marvell_drivers
ffffffff812afc00 d marvell_hwmon_info
ffffffff812d2bb8 d marvell_enable
ffffffff812d3000 d marvell_hwmon_temp_config
ffffffff812d3008 d marvell_hwmon_chip_config
[root@centos7 linux-5.14]# 
# ls /sys/bus/
amba         cpu          mdio_bus     pci          scsi         usb
clockevents  gpio         mmc          pci_express  sdio         virtio
clocksource  hid          mmc_rpmb     platform     serio        workqueue
container    i2c          nvmem        rpmsg        spi
# 
# ls /sys/bus/gpio/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/gpio/devices/
# 
# ls /sys/bus/modio_bus
ls: /sys/bus/modio_bus: No such file or directory
# 
# ls /sys/bus/mdio_bus/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/mdio_bus/devices/
# 
# ls /sys/bus/p
pci/          pci_express/  platform/
# ls /sys/bus/platform/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/platform/devices/
1000.rom                        3000.error-device
1700000.bus-error-unit          4000.teststatus
2000000.clint                   Fixed MDIO bus.0
20000000.serial                 a000000.rom
20010000.spi                    serial8250
20040000.i2c                    soc
2010000.cache-controller        soc:global-external-interrupts
# 
# 

mdio bus

[ 6.324212] libphy: Fixed MDIO Bus: probed
static int __init fixed_mdio_bus_init(void)
{
        struct fixed_mdio_bus *fmb = &platform_fmb;
        int ret;

        pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
        if (IS_ERR(pdev))
                return PTR_ERR(pdev);

        fmb->mii_bus = mdiobus_alloc();
        if (fmb->mii_bus == NULL) {
                ret = -ENOMEM;
                goto err_mdiobus_reg;
        }

        snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
        fmb->mii_bus->name = "Fixed MDIO Bus";
        fmb->mii_bus->priv = fmb;
        fmb->mii_bus->parent = &pdev->dev;
        fmb->mii_bus->read = &fixed_mdio_read;
        fmb->mii_bus->write = &fixed_mdio_write;

        ret = mdiobus_register(fmb->mii_bus);
        if (ret)
                goto err_mdiobus_alloc;

        return 0;

err_mdiobus_alloc:
        mdiobus_free(fmb->mii_bus);
err_mdiobus_reg:
        platform_device_unregister(pdev);
        return ret;
}
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
        struct mdio_device *mdiodev;
        int i, err;
        struct gpio_desc *gpiod;

        if (NULL == bus || NULL == bus->name ||
            NULL == bus->read || NULL == bus->write)
                return -EINVAL;

        BUG_ON(bus->state != MDIOBUS_ALLOCATED &&
               bus->state != MDIOBUS_UNREGISTERED);

        bus->owner = owner;
        bus->dev.parent = bus->parent;
        bus->dev.class = &mdio_bus_class;
        bus->dev.groups = NULL;
        dev_set_name(&bus->dev, "%s", bus->id);

        err = device_register(&bus->dev);
        if (err) {
                pr_err("mii_bus %s failed to register
", bus->id);
                return -EINVAL;
        }

        mutex_init(&bus->mdio_lock);
        mutex_init(&bus->shared_lock);

        /* assert bus level PHY GPIO reset */
        gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(gpiod)) {
                err = dev_err_probe(&bus->dev, PTR_ERR(gpiod),
                                    "mii_bus %s couldn't get reset GPIO
",
                                    bus->id);
                device_del(&bus->dev);
                return err;
        } else  if (gpiod) {
                bus->reset_gpiod = gpiod;
                fsleep(bus->reset_delay_us);
                gpiod_set_value_cansleep(gpiod, 0);
                if (bus->reset_post_delay_us > 0)
                        fsleep(bus->reset_post_delay_us);
        }

        if (bus->reset) {
                err = bus->reset(bus);
                if (err)
                        goto error_reset_gpiod;
        }

        for (i = 0; i < PHY_MAX_ADDR; i++) {
                if ((bus->phy_mask & (1 << i)) == 0) {
                        struct phy_device *phydev;

                        phydev = mdiobus_scan(bus, i);
                        if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV)) {
                                err = PTR_ERR(phydev);
                                goto error;
                        }
                }
        }

        mdiobus_setup_mdiodev_from_board_info(bus, mdiobus_create_device);

        bus->state = MDIOBUS_REGISTERED;
        pr_info("%s: probed
", bus->name);
        return 0;
 -DKBUILD_MODFILE='"drivers/net/phy/libphy"' -DKBUILD_BASENAME='"phy_device"' -DKBUILD_MODNAME='"libphy"' -D__KBUILD_MODNAME=kmod_libphy -c -o drivers/net/phy/phy_device.o drivers/net/phy/phy_device.c
drivers/net/phy/Makefile:4:libphy-y                     := phy.o phy-c45.o phy-core.o phy_device.o 
drivers/net/phy/Makefile:14:# dedicated loadable module, so we bundle them all together into libphy.ko
drivers/net/phy/Makefile:16:libphy-y                    += $(mdio-bus-y)
drivers/net/phy/Makefile:21:libphy-$(CONFIG_SWPHY)              += swphy.o
drivers/net/phy/Makefile:22:libphy-$(CONFIG_LED_TRIGGER_PHY)    += phy_led_triggers.o
drivers/net/phy/Makefile:25:obj-$(CONFIG_PHYLIB)                += libphy.o

phy_driver_register

 */
int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
        int retval;

        /* Either the features are hard coded, or dynamically
         * determined. It cannot be both.
         */
        if (WARN_ON(new_driver->features && new_driver->get_features)) {
                pr_err("%s: features and get_features must not both be set
",
                       new_driver->name);
                return -EINVAL;
        }

        new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
        new_driver->mdiodrv.driver.name = new_driver->name;
        new_driver->mdiodrv.driver.bus = &mdio_bus_type;
        new_driver->mdiodrv.driver.probe = phy_probe;
        new_driver->mdiodrv.driver.remove = phy_remove;
        new_driver->mdiodrv.driver.shutdown = phy_shutdown;
        new_driver->mdiodrv.driver.owner = owner;
        new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;

        retval = driver_register(&new_driver->mdiodrv.driver);
        if (retval) {
                pr_err("%s: Error %d in registering driver
",
                       new_driver->name, retval);

                return retval;
        }

        pr_err("%s: Registered new driver
", new_driver->name);
        //pr_debug("%s: Registered new driver
", new_driver->name);

        return 0;
}

# 
# ls /sys/bus/
amba         cpu          mdio_bus     pci          scsi         usb
clockevents  gpio         mmc          pci_express  sdio         virtio
clocksource  hid          mmc_rpmb     platform     serio        workqueue
container    i2c          nvmem        rpmsg        spi
# 
# ls /sys/bus/mdio_bus/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/mdio_bus/devices/
# 
# ls /sys/bus/mdio_bus/drivers
Generic Clause 45 PHY      Marvell 88E1240
Generic PHY                Marvell 88E1318S
Marvell 88E1101            Marvell 88E1340S
Marvell 88E1111            Marvell 88E1510
Marvell 88E1111 (Finisar)  Marvell 88E1540
Marvell 88E1112            Marvell 88E1545
Marvell 88E1116R           Marvell 88E1548P
Marvell 88E1118            Marvell 88E3016
Marvell 88E1121R           Marvell 88E6341 Family
Marvell 88E1145            Marvell 88E6390 Family
Marvell 88E1149R           Marvell 88E6393 Family
# 
# 
[    3.334158] [<c04719cc>] (rpm_resume) from [<c0471e98>] (__pm_runtime_resume+0x4c/0x64)
[    3.342502] [<c0471e98>] (__pm_runtime_resume) from [<c055c08c>] (davinci_mdio_reset+0x14/0x108)
[    3.351666] [<c055c08c>] (davinci_mdio_reset) from [<c0510eb4>] (__mdiobus_register+0xb8/0x198)
[    3.360732] [<c0510eb4>] (__mdiobus_register) from [<c0675640>] (of_mdiobus_register+0x34/0x1b4)
[    3.369891] [<c0675640>] (of_mdiobus_register) from [<c055c620>] (davinci_mdio_probe+0x4a0/0x4e0)
[    3.379146] [<c055c620>] (davinci_mdio_probe) from [<c046a648>] (platform_drv_probe+0x50/0xb0)
[    3.388122] [<c046a648>] (platform_drv_probe) from [<c046913c>] (driver_probe_device+0x204/0x2b0)
[    3.397368] [<c046913c>] (driver_probe_device) from [<c04675e0>] (bus_for_each_drv+0x60/0x94)
[    3.406251] [<c04675e0>] (bus_for_each_drv) from [<c0468e58>] (__device_attach+0xb0/0x114)
[    3.414868] [<c0468e58>] (__device_attach) from [<c04683bc>] (bus_probe_device+0x84/0x8c)
[    3.423391] [<c04683bc>] (bus_probe_device) from [<c04667a4>] (device_add+0x3dc/0x568)
[    3.431643] [<c04667a4>] (device_add) from [<c0671124>] (of_platform_device_create_pdata+0x84/0xbc)
[    3.441076] [<c0671124>] (of_platform_device_create_pdata) from [<c067137c>] (of_platform_bus_create+0xe8/0x1e4)
[    3.451682] [<c067137c>] (of_platform_bus_create) from [<c06715e0>] (of_platform_populate+0x6c/0xcc)
[    3.461201] [<c06715e0>] (of_platform_populate) from [<c067fcd0>] (pruss_probe+0x230/0x2f4)
[    3.469901] [<c067fcd0>] (pruss_probe) from [<c046a648>] (platform_drv_probe+0x50/0xb0)
[    3.478238] [<c046a648>] (platform_drv_probe) from [<c046913c>] (driver_probe_device+0x204/0x2b0)
[    3.487485] [<c046913c>] (driver_probe_device) from [<c04692a0>] (__driver_attach+0xb8/0xbc)
[    3.496276] [<c04692a0>] (__driver_attach) from [<c0467538>] (bus_for_each_dev+0x68/0x9c)
[    3.504798] [<c0467538>] (bus_for_each_dev) from [<c04685d0>] (bus_add_driver+0x108/0x214)
[    3.513413] [<c04685d0>] (bus_add_driver) from [<c0469778>] (driver_register+0x78/0xf4)
[    3.521757] [<c0469778>] (driver_register) from [<c01017dc>] (do_one_initcall+0x44/0x16c)
[    3.530280] [<c01017dc>] (do_one_initcall) from [<c0c00dc0>] (kernel_init_freeable+0x158/0x1e8)
[    3.539343] [<c0c00dc0>] (kernel_init_freeable) from [<c07fc680>] (kernel_init+0x8/0x110)
[    3.547868] [<c07fc680>] (kernel_init) from [<c0107678>] (ret_from_fork+0x14/0x3c)
[    3.555760] ---[ end trace 4c8f32c91d7d69e1 ]---

 调用match

Linux设备驱动模型简述(源码剖析)

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    struct device_attach_data *data = _data;
    struct device *dev = data->dev;
    bool async_allowed;
    int ret;

    ret = driver_match_device(drv, dev);
    if (ret == 0) {
        /* no match */
        return 0;
    } else if (ret == -EPROBE_DEFER) {
        dev_dbg(dev, "Device match requests probe deferral
");
        driver_deferred_probe_add(dev);
    } else if (ret < 0) {
        dev_dbg(dev, "Bus failed to match device: %d", ret);
        return ret;
    } /* ret > 0 means positive match */

    async_allowed = driver_allows_async_probing(drv);

    if (async_allowed)
        data->have_async = true;

    if (data->check_async && async_allowed != data->want_async)
        return 0;

    return driver_probe_device(drv, dev);
}

bus_probe_device(&globalfifo_device.dev)的执行函数路线分析如下所示,经过层层调用,最终又调用到driver_match_device()driver_probe_device()函数,查找总线上能和当前设备匹配的驱动,并将驱动和设备绑定在了一起。

 struct device *dev = &globalfifo_device.dev;
    struct device_attach_data *data = {
        .dev = dev,
        .check_async = allow_async,
        .want_async = false,
    };
    struct device_driver *drv;
---------------------------------------------------
        bus_probe_device(dev)
                |
                V
        device_initial_probe(dev)
                |
                V
        __device_attach(dev, true) 
                |
                V
        bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver)
                |
                V
        __device_attach_driver(drv, data)
                |
                V
        driver_match_device(drv, dev) / driver_probe_device(drv, dev)

调用probe

static int call_driver_probe(struct device *dev, struct device_driver *drv)
{
        int ret = 0;

  
        if (dev->bus->probe)
                ret = dev->bus->probe(dev);
        else if (drv->probe)
                ret = drv->probe(dev);

        switch (ret) {
        case 0:
                break;
        case -EPROBE_DEFER:
                /* Driver requested deferred probing */
                dev_dbg(dev, "Driver %s requests probe deferral
", drv->name);
                break;
        case -ENODEV:
        case -ENXIO:
                pr_debug("%s: probe of %s rejects match %d
",
                         drv->name, dev_name(dev), ret);
                break;
        default:
                /* driver matched but the probe failed */
                pr_warn("%s: probe of %s failed with error %d
",
                        drv->name, dev_name(dev), ret);

 device_add(struct device *dev)

drivers/base/core.c

# dmesg | grep mdio
[    0.926109] device: 'soc:mdio': device_add
[    0.934012] bus: 'platform': tracing bus_probe_device  soc:mdio
# 
# 
void bus_probe_device(struct device *dev)
{
        struct bus_type *bus = dev->bus;
        struct subsys_interface *sif;

        if (!bus)
                return;

        pr_err("bus: '%s': tracing bus_probe_device  %s
", bus->name, dev_name(dev));
        if (bus->p->drivers_autoprobe)
                device_initial_probe(dev);

        mutex_lock(&bus->p->mutex);
        list_for_each_entry(sif, &bus->p->interfaces, node)
                if (sif->add_dev)
                        sif->add_dev(dev, sif);
        mutex_unlock(&bus->p->mutex);
}


# ls  /sys/bus/platform/devices/
1000.rom                        4000.teststatus
1700000.bus-error-unit          Fixed MDIO bus.0
2000000.clint                   a000000.rom
20000000.serial                 serial8250
20010000.spi                    soc
20040000.i2c                    soc:global-external-interrupts
2010000.cache-controller        soc:mdio
3000.error-device
# 
# ls  /sys/bus/platform/drivers
ahci                 ohci-platform        sifive_spi
alarmtimer           pci-host-generic     simple-reset
dw-apb-uart          pwrseq_emmc          syscon
ehci-platform        pwrseq_simple        syscon-poweroff
goldfish_rtc         sdhci-cdns           syscon-reboot
gpio-clk             serial8250           xhci-hcd
of_fixed_clk         sifive-clk-prci      xilinx-pcie
of_fixed_factor_clk  sifive-serial
of_serial            sifive_gpio
# 
#

device_initial_probe

void device_initial_probe(struct device *dev)
{
        __device_attach(dev, true);
}

phy_device_register

stmmac_dvr_probe ->  stmmac_mdio_register ->  of_mdiobus_register ->  mdiobus_register ->  __mdiobus_register ->  mdiobus_scan ->  get_phy_device &  phy_device_register

识别phy的过程:
在网口驱动的probe中,调用mdiobus_register;
在mdiobus_register函数中,会对从0到PHY_MAX_ADDR(一般是31)依次调用mdiobus_scan;
在mdiobus_scan中会调用get_phy_device,如果返回成功,则调用phy_device_register;
在get_phy_device中,会调用get_phy_id来读取对应地址上的phy_id,然后如果满足((phy_id & 0x1fffffff) == 0x1fffffff),则认识该phy不存在。
然后在port的probe中会调用phy_connect来连接phy;
在phy_connect中,会调用bus_find_device_by_name来查找对应的phy是否存在。存在则connect_direct。
在/sys/bus/mdio_bus/devices/下有当前内核扫描到的所有PHY

struct bus_type mdio_bus_type = {
        .name           = "mdio_bus",
        .dev_groups     = mdio_bus_dev_groups,
        .match          = mdio_bus_match,
        .uevent         = mdio_uevent,
};
EXPORT_SYMBOL(mdio_bus_type);

int __init mdio_bus_init(void)
{
        int ret;

        ret = class_register(&mdio_bus_class);
        if (!ret) {
                ret = bus_register(&mdio_bus_type);
                if (ret)
                        class_unregister(&mdio_bus_class);
        }

        return ret;
}
mdiobus_register
int mdiobus_register(struct mii_bus *bus)
{
    int i, err;
    if (NULL == bus || NULL == bus->name || NULL == bus->read ||NULL == bus->write)
        return -EINVAL;
    BUG_ON(bus->state != MDIOBUS_ALLOCATED &&bus->state != MDIOBUS_UNREGISTERED);
    bus->dev.parent = bus->parent;
    bus->dev.class = &mdio_bus_class;    //总线设备类"/sys/bus/mdio_bus"
    bus->dev.groups = NULL;
    dev_set_name(&bus->dev, "%s", bus->id);    //设置总线设备名
    err = device_register(&bus->dev);    //注册设备文件
    if (err) {
        printk(KERN_ERR "mii_bus %s failed to register
", bus->id);
        return -EINVAL;
    }
    mutex_init(&bus->mdio_lock);
    if (bus->reset)
        bus->reset(bus);    //总线复位
    for (i = 0; i < PHY_MAX_ADDR; i++) {
        if ((bus->phy_mask & (1 << i)) == 0) {
            struct phy_device *phydev;
            phydev = mdiobus_scan(bus, i);    //扫描phy设备
            if (IS_ERR(phydev)) {
                err = PTR_ERR(phydev);
                goto error;
            }
        }
    }
    bus->state = MDIOBUS_REGISTERED;    //状态设置为已注册
    pr_info("%s: probed
", bus->name);
    return 0;
error:
    while (--i >= 0) {
        if (bus->phy_map[i])
            device_unregister(&bus->phy_map[i]->dev);
    }
    device_del(&bus->dev);
    return err;
}
EXPORT_SYMBOL(mdiobus_register);

mdiobus_scan

struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
    struct phy_device *phydev;
    int err;
    phydev = get_phy_device(bus, addr);    //获取创建phy设备
    if (IS_ERR(phydev) || phydev == NULL)
        return phydev;
    err = phy_device_register(phydev);    //注册phy设备
    if (err) {
        phy_device_free(phydev);
        return NULL;
    }
    return phydev;
}
EXPORT_SYMBOL(mdiobus_scan);

phy_device_create mdiobus_register_device

mdiobus_register_device 会调用device_add匹配驱动

struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
        struct phy_c45_device_ids c45_ids;
        u32 phy_id = 0;
        int r;

        c45_ids.devices_in_package = 0;
        c45_ids.mmds_present = 0;
        memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));

        if (is_c45)
                r = get_phy_c45_ids(bus, addr, &c45_ids);
        else
                r = get_phy_c22_id(bus, addr, &phy_id);

        if (r)
                return ERR_PTR(r);

        /* PHY device such as the Marvell Alaska 88E2110 will return a PHY ID
         * of 0 when probed using get_phy_c22_id() with no error. Proceed to
         * probe with C45 to see if we're able to get a valid PHY ID in the C45
         * space, if successful, create the C45 PHY device.
         */
        if (!is_c45 && phy_id == 0 && bus->probe_capabilities >= MDIOBUS_C45) {
                r = get_phy_c45_ids(bus, addr, &c45_ids);
                if (!r)
                        return phy_device_create(bus, addr, phy_id,
                                                 true, &c45_ids);
        }

        return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}


int phy_device_register(struct phy_device *phydev)
{
        int err;

        err = mdiobus_register_device(&phydev->mdio);
        if (err)
                return err;

        /* Deassert the reset signal */
        phy_device_reset(phydev, 0);

        /* Run all of the fixups for this PHY */
        err = phy_scan_fixups(phydev);
        if (err) {
                phydev_err(phydev, "failed to initialize
");
                goto out;
        }

        err = device_add(&phydev->mdio.dev);
        if (err) {
                phydev_err(phydev, "failed to add
");
                goto out;
        }

        return 0;

 out:
        /* Assert the reset signal */
        phy_device_reset(phydev, 1);

        mdiobus_unregister_device(&phydev->mdio);
        return err;
}

mii、mdio、phy、mac关系图

 drivers/net/ethernet/ti/davinci_mdio.c

[    3.326542] [<c010bc4c>] (__irq_svc) from [<c04719cc>] (rpm_resume+0x158/0x5d8)
[    3.334158] [<c04719cc>] (rpm_resume) from [<c0471e98>] (__pm_runtime_resume+0x4c/0x64)
[    3.342502] [<c0471e98>] (__pm_runtime_resume) from [<c055c08c>] (davinci_mdio_reset+0x14/0x108)
[    3.351666] [<c055c08c>] (davinci_mdio_reset) from [<c0510eb4>] (__mdiobus_register+0xb8/0x198)
[    3.360732] [<c0510eb4>] (__mdiobus_register) from [<c0675640>] (of_mdiobus_register+0x34/0x1b4)
[    3.369891] [<c0675640>] (of_mdiobus_register) from [<c055c620>] (davinci_mdio_probe+0x4a0/0x4e0)
[    3.379146] [<c055c620>] (davinci_mdio_probe) from [<c046a648>] (platform_drv_probe+0x50/0xb0)
[    3.388122] [<c046a648>] (platform_drv_probe) from [<c046913c>] (driver_probe_device+0x204/0x2b0)
[    3.397368] [<c046913c>] (driver_probe_device) from [<c04675e0>] (bus_for_each_drv+0x60/0x94)
[    3.406251] [<c04675e0>] (bus_for_each_drv) from [<c0468e58>] (__device_attach+0xb0/0x114)
[    3.414868] [<c0468e58>] (__device_attach) from [<c04683bc>] (bus_probe_device+0x84/0x8c)
[    3.423391] [<c04683bc>] (bus_probe_device) from [<c04667a4>] (device_add+0x3dc/0x568)
[    3.431643] [<c04667a4>] (device_add) from [<c0671124>] (of_platform_device_create_pdata+0x84/0xbc)
[    3.441076] [<c0671124>] (of_platform_device_create_pdata) from [<c067137c>] (of_platform_bus_create+0xe8/0x1e4)
[    3.451682] [<c067137c>] (of_platform_bus_create) from [<c06715e0>] (of_platform_populate+0x6c/0xcc)
[    3.461201] [<c06715e0>] (of_platform_populate) from [<c067fcd0>] (pruss_probe+0x230/0x2f4)
[    3.469901] [<c067fcd0>] (pruss_probe) from [<c046a648>] (platform_drv_probe+0x50/0xb0)
[    3.478238] [<c046a648>] (platform_drv_probe) from [<c046913c>] (driver_probe_device+0x204/0x2b0)
[    3.487485] [<c046913c>] (driver_probe_device) from [<c04692a0>] (__driver_attach+0xb8/0xbc)
[    3.496276] [<c04692a0>] (__driver_attach) from [<c0467538>] (bus_for_each_dev+0x68/0x9c)
[    3.504798] [<c0467538>] (bus_for_each_dev) from [<c04685d0>] (bus_add_driver+0x108/0x214)
[    3.513413] [<c04685d0>] (bus_add_driver) from [<c0469778>] (driver_register+0x78/0xf4)
[    3.521757] [<c0469778>] (driver_register) from [<c01017dc>] (do_one_initcall+0x44/0x16c)
[    3.530280] [<c01017dc>] (do_one_initcall) from [<c0c00dc0>] (kernel_init_freeable+0x158/0x1e8)
[    3.539343] [<c0c00dc0>] (kernel_init_freeable) from [<c07fc680>] (kernel_init+0x8/0x110)
[    3.547868] [<c07fc680>] (kernel_init) from [<c0107678>] (ret_from_fork+0x14/0x3c)


 

drivers/net/ethernet/marvell/

cadence

static const struct of_device_id macb_dt_ids[] = {
        { .compatible = "cdns,at32ap7000-macb" },
        { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
        { .compatible = "cdns,macb" },
        { .compatible = "cdns,np4-macb", .data = &np4_config },
        { .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
        { .compatible = "cdns,gem", .data = &pc302gem_config },
        { .compatible = "cdns,sam9x60-macb", .data = &at91sam9260_config },
        { .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config },
        { .compatible = "atmel,sama5d3-gem", .data = &sama5d3_config },
        { .compatible = "atmel,sama5d3-macb", .data = &sama5d3macb_config },
        { .compatible = "atmel,sama5d4-gem", .data = &sama5d4_config },
        { .compatible = "cdns,at91rm9200-emac", .data = &emac_config },
        { .compatible = "cdns,emac", .data = &emac_config },
        { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config},
        { .compatible = "cdns,zynq-gem", .data = &zynq_config },
        { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config },
        { .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config },
        { .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config },
        { /* sentinel */ }
};
 root@ubuntu:~# ls /sys/bus/
clockevents/  hid/          pci_express/  spi/
clocksource/  i2c/          platform/     usb/
container/    mdio_bus/     rpmsg/        usb-serial/
cpu/          mmc/          scsi/         virtio/
edac/         mmc_rpmb/     sdio/         workqueue/
event_source/ nvmem/        serio/        
gpio/         pci/          snd_seq/      
root@ubuntu:~# ls /sys/bus/mdio_bus/
devices  drivers  drivers_autoprobe  drivers_probe  uevent
root@ubuntu:~# ls /sys/bus/mdio_bus/devices/
10090000.ethernet-ffffffff:00
root@ubuntu:~# ls /sys/bus/mdio_bus/drivers
'Generic Clause 45 PHY'       'Microsemi GE VSC8504 SyncE'  'Microsemi GE VSC8575 SyncE'  'RTL8201F Fast Ethernet'      'RTL8211E Gigabit Ethernet'   'RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY'
'Generic FE-GE Realtek PHY'   'Microsemi GE VSC8514 SyncE'  'Microsemi GE VSC8582 SyncE'  'RTL8208 Fast Ethernet'       'RTL8211F Gigabit Ethernet'   'RTL8226B_RTL8221B 2.5Gbps PHY'
'Generic PHY'                 'Microsemi GE VSC8552 SyncE'  'Microsemi GE VSC8584 SyncE'  'RTL8211 Gigabit Ethernet'    'RTL8221B-VB-CG 2.5Gbps PHY'  'RTL8366RB Gigabit Ethernet'
'Microsemi FE VSC8530'        'Microsemi GE VSC856X SyncE'  'Microsemi VSC8531'           'RTL8211B Gigabit Ethernet'   'RTL8221B-VM-CG 2.5Gbps PHY'  'RTL9000AA_RTL9000AN Ethernet'
'Microsemi FE VSC8540 SyncE'  'Microsemi GE VSC8572 SyncE'  'Microsemi VSC8541 SyncE'     'RTL8211C Gigabit Ethernet'   'RTL8226 2.5Gbps PHY'
'Microsemi GE VSC8502 SyncE'  'Microsemi GE VSC8574 SyncE'  'RTL8201CP Ethernet'          'RTL8211DN Gigabit Ethernet'  'RTL8226-CG 2.5Gbps PHY'
root@ubuntu:~# ls /sys/bus/platform/
devices  drivers  drivers_autoprobe  drivers_probe  uevent
root@ubuntu:~# ls /sys/bus/platform/devices/
 10000000.clock-controller   10011000.serial   10021000.pwm   10040000.spi   10060000.gpio       2010000.cache-controller  'Fixed MDIO bus.0'   gpio-restart   sifive_edac.0   soc:pwmleds
 10010000.serial             10020000.pwm      10030000.i2c   10050000.spi   10090000.ethernet   3000000.dma                cpufreq-dt          serial8250     soc
root@ubuntu:~# ls /sys/bus/platform/drivers
ahci        fu740-pcie    leds_pwm    of_fixed_clk         pci-host-generic  poweroff-restart  pwrseq_emmc    sata_mv     sifive-clk-prci  sifive_spi    syscon-poweroff  xilinx-pcie
alarmtimer  goldfish_rtc  macb        of_fixed_factor_clk  physmap-flash     pwm-clock         pwrseq_simple  serial8250  sifive-serial    simple-reset  syscon-reboot
cpufreq-dt  gpio-clk      ocores-i2c  of_serial            poweroff-gpio     pwm-sifive        restart-gpio   sf-pdma     sifive_gpio      syscon        virtio-mmio
root@ubuntu:~# 

ls  /sys/bus/platform/devices/ -al
root@ubuntu:~# ls  /sys/bus/platform/devices/ -al
total 0
drwxr-xr-x 2 root root 0 Jul 21 19:00  .
drwxr-xr-x 4 root root 0 Jul 21 19:00  ..
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10000000.clock-controller -> ../../../devices/platform/soc/10000000.clock-controller
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10010000.serial -> ../../../devices/platform/soc/10010000.serial
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10011000.serial -> ../../../devices/platform/soc/10011000.serial
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10020000.pwm -> ../../../devices/platform/soc/10020000.pwm
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10021000.pwm -> ../../../devices/platform/soc/10021000.pwm
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10030000.i2c -> ../../../devices/platform/soc/10030000.i2c
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10040000.spi -> ../../../devices/platform/soc/10040000.spi
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10050000.spi -> ../../../devices/platform/soc/10050000.spi
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10060000.gpio -> ../../../devices/platform/soc/10060000.gpio
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10090000.ethernet -> ../../../devices/platform/soc/10090000.ethernet
lrwxrwxrwx 1 root root 0 Jul 21 19:00  2010000.cache-controller -> ../../../devices/platform/soc/2010000.cache-controller
lrwxrwxrwx 1 root root 0 Jul 21 19:00  3000000.dma -> ../../../devices/platform/soc/3000000.dma
lrwxrwxrwx 1 root root 0 Jul 21 19:00 'Fixed MDIO bus.0' -> '../../../devices/platform/Fixed MDIO bus.0'
lrwxrwxrwx 1 root root 0 Jul 21 19:00  cpufreq-dt -> ../../../devices/platform/cpufreq-dt
lrwxrwxrwx 1 root root 0 Jul 21 19:00  gpio-restart -> ../../../devices/platform/gpio-restart
lrwxrwxrwx 1 root root 0 Jul 21 19:00  serial8250 -> ../../../devices/platform/serial8250
lrwxrwxrwx 1 root root 0 Jul 21 19:00  sifive_edac.0 -> ../../../devices/platform/sifive_edac.0
lrwxrwxrwx 1 root root 0 Jul 21 19:00  soc -> ../../../devices/platform/soc
lrwxrwxrwx 1 root root 0 Jul 21 19:00  soc:pwmleds -> ../../../devices/platform/soc/soc:pwmleds
root@ubuntu:~# ls  /sys/bus/platform/devices/soc
10000000.clock-controller  10011000.serial  10021000.pwm  10040000.spi  10060000.gpio      2010000.cache-controller  driver_override  of_node      subsystem  waiting_for_supplier
10010000.serial            10020000.pwm     10030000.i2c  10050000.spi  10090000.ethernet  3000000.dma               modalias         soc:pwmleds  uevent
root@ubuntu:~# ls  /sys/bus/platform/devices/soc/10090000.ethernet/
driver  driver_override  mdio_bus  modalias  net  of_node  subsystem  supplier:platform:10000000.clock-controller  uevent
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node
cat: /sys/bus/platform/devices/soc/10090000.ethernet/of_node: Is a directory
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/
#address-cells     clock-names        compatible         interrupt-parent   local-mac-address  phy-handle         reg                
#size-cells        clocks             ethernet-phy@0/    interrupts         name               phy-mode           status             
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/compatible 
sifive,fu540-c000-gemroot@ubuntu:~# 
root@ubuntu:~# 
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/reg 
      
root@ubuntu:~# 
root@ubuntu:~# ls  /sys/bus/platform/devices/ -al
total 0
drwxr-xr-x 2 root root 0 Jul 21 19:00  .
drwxr-xr-x 4 root root 0 Jul 21 19:00  ..
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10000000.clock-controller -> ../../../devices/platform/soc/10000000.clock-controller
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10010000.serial -> ../../../devices/platform/soc/10010000.serial
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10011000.serial -> ../../../devices/platform/soc/10011000.serial
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10020000.pwm -> ../../../devices/platform/soc/10020000.pwm
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10021000.pwm -> ../../../devices/platform/soc/10021000.pwm
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10030000.i2c -> ../../../devices/platform/soc/10030000.i2c
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10040000.spi -> ../../../devices/platform/soc/10040000.spi
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10050000.spi -> ../../../devices/platform/soc/10050000.spi
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10060000.gpio -> ../../../devices/platform/soc/10060000.gpio
lrwxrwxrwx 1 root root 0 Jul 21 19:00  10090000.ethernet -> ../../../devices/platform/soc/10090000.ethernet
lrwxrwxrwx 1 root root 0 Jul 21 19:00  2010000.cache-controller -> ../../../devices/platform/soc/2010000.cache-controller
lrwxrwxrwx 1 root root 0 Jul 21 19:00  3000000.dma -> ../../../devices/platform/soc/3000000.dma
lrwxrwxrwx 1 root root 0 Jul 21 19:00 'Fixed MDIO bus.0' -> '../../../devices/platform/Fixed MDIO bus.0'
lrwxrwxrwx 1 root root 0 Jul 21 19:00  cpufreq-dt -> ../../../devices/platform/cpufreq-dt
lrwxrwxrwx 1 root root 0 Jul 21 19:00  gpio-restart -> ../../../devices/platform/gpio-restart
lrwxrwxrwx 1 root root 0 Jul 21 19:00  serial8250 -> ../../../devices/platform/serial8250
lrwxrwxrwx 1 root root 0 Jul 21 19:00  sifive_edac.0 -> ../../../devices/platform/sifive_edac.0
lrwxrwxrwx 1 root root 0 Jul 21 19:00  soc -> ../../../devices/platform/soc
lrwxrwxrwx 1 root root 0 Jul 21 19:00  soc:pwmleds -> ../../../devices/platform/soc/soc:pwmleds
root@ubuntu:~# ls  /sys/bus/platform/devices/soc
10000000.clock-controller  10011000.serial  10021000.pwm  10040000.spi  10060000.gpio      2010000.cache-controller  driver_override  of_node      subsystem  waiting_for_supplier
10010000.serial            10020000.pwm     10030000.i2c  10050000.spi  10090000.ethernet  3000000.dma               modalias         soc:pwmleds  uevent
root@ubuntu:~# ls  /sys/bus/platform/devices/soc/10090000.ethernet/
driver  driver_override  mdio_bus  modalias  net  of_node  subsystem  supplier:platform:10000000.clock-controller  uevent
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node
cat: /sys/bus/platform/devices/soc/10090000.ethernet/of_node: Is a directory
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/
#address-cells     clock-names        compatible         interrupt-parent   local-mac-address  phy-handle         reg                
#size-cells        clocks             ethernet-phy@0/    interrupts         name               phy-mode           status             
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/compatible 
sifive,fu540-c000-gemroot@ubuntu:~# 
root@ubuntu:~# 
root@ubuntu:~# cat   /sys/bus/platform/devices/soc/10090000.ethernet/of_node/reg 
      
root@ubuntu:~# 

macb: probe of 23000000.ethernet failed with error -22

[    7.458593] bus: 'platform': __driver_probe_device: matched device 23000000.ethernet with driver macb
[    7.472258] macb: call_driver_probe of 23000000.ethernet 
[    7.480418] macb 23000000.ethernet: invalid resource
[    7.487837] macb: probe of 23000000.ethernet failed with error -22

 

# cat /proc/i
interrupts  iomem       ioports     irq/
# cat /proc/iomem 
20000000-2000001f : serial
20020000-2002ffff : timer@20020000
20030000-2003ffff : watchdog@20030000
  20030000-2003ffff : 20030000.watchdog watchdog@20030000
20050000-2005ffff : gpio@20050000
80000000-8003ffff : Reserved
80200000-87ffffff : System RAM
  80202000-815169a4 : Kernel image
    80202000-80786aed : Kernel code
    81000000-811fffff : Kernel rodata
    81400000-814d33ff : Kernel data
    814d4000-815169a4 : Kernel bss
# 
# cat /proc/interrupts 
           CPU0       
  1:      61778  SiFive PLIC   7  timer
  5:          1  RISC-V INTC   5  riscv-timer
 11:        418  SiFive PLIC   5  ttyS0
IPI0:         0  Rescheduling interrupts
IPI1:         0  Function call interrupts
IPI2:         0  CPU stop interrupts
IPI3:         0  IRQ work interrupts
IPI4:         0  Timer broadcast interrupts
# 
# cat /proc/ioports 
# 
# 

调用devm_platform_get_and_ioremap_resource(pdev, 0, &regs);失败

static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
        int ret = 0;

        if (dev->p->dead || !device_is_registered(dev))
                return -ENODEV;
        if (dev->driver)
                return -EBUSY;

        dev->can_match = true;
       
        pr_err("bus: '%s': %s: matched device %s with driver %s
",
                 drv->bus->name, __func__, dev_name(dev), drv->name);

        pm_runtime_get_suppliers(dev);
        if (dev->parent)
                pm_runtime_get_sync(dev->parent);

        pm_runtime_barrier(dev);
        if (initcall_debug)
                ret = really_probe_debug(dev, drv);
        else
                ret = really_probe(dev, drv);
        pm_request_idle(dev);

        if (dev->parent)
                pm_runtime_put(dev->parent);

        pm_runtime_put_suppliers(dev);
        return ret;
}
static int call_driver_probe(struct device *dev, struct device_driver *drv)
{
        int ret = 0;

        pr_warn("%s: call_driver_probe of %s 
",
                        drv->name, dev_name(dev));
        if (dev->bus->probe)
                ret = dev->bus->probe(dev);
        else if (drv->probe)
                ret = drv->probe(dev);

        switch (ret) {
        case 0:
                break;
        case -EPROBE_DEFER:
                /* Driver requested deferred probing */
                dev_dbg(dev, "Driver %s requests probe deferral
", drv->name);
                break;
        case -ENODEV:
        case -ENXIO:
                pr_debug("%s: probe of %s rejects match %d
",
                         drv->name, dev_name(dev), ret);
                break;
        default:
                /* driver matched but the probe failed */
                pr_warn("%s: probe of %s failed with error %d
",
                        drv->name, dev_name(dev), ret);
                break;
        }

        return ret;
}
# ls /sys/bus/platform/devices/ -al
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 .
drwxr-xr-x    4 root     root             0 Jan  1 00:00 ..
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 1000.rom -> ../../../devices/platform/soc/1000.rom
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 1700000.bus-error-unit -> ../../../devices/platform/soc/1700000.bus-error-unit
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 2000000.clint -> ../../../devices/platform/soc/2000000.clint
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20000000.serial -> ../../../devices/platform/soc/20000000.serial
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20010000.spi -> ../../../devices/platform/soc/20010000.spi
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20040000.i2c -> ../../../devices/platform/soc/20040000.i2c
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 2010000.cache-controller -> ../../../devices/platform/soc/2010000.cache-controller
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 23000000.ethernet -> ../../../devices/platform/soc/23000000.ethernet
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 3000.error-device -> ../../../devices/platform/soc/3000.error-device
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 4000.teststatus -> ../../../devices/platform/soc/4000.teststatus
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 Fixed MDIO bus.0 -> ../../../devices/platform/Fixed MDIO bus.0
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 a000000.rom -> ../../../devices/platform/soc/a000000.rom
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 serial8250 -> ../../../devices/platform/serial8250
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc -> ../../../devices/platform/soc
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc:global-external-interrupts -> ../../../devices/platform/soc/soc:global-external-interrupts
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc:mdio -> ../../../devices/platform/soc/soc:mdio
# 
# ls /sys/bus/platform/devices/23000000.ethernet/
driver_override       of_node               uevent
modalias              subsystem             waiting_for_supplier
# 
# ls /sys/bus/platform/devices/23000000.ethernet/subsystem/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/platform/devices/23000000.ethernet/subsystem/devices/ -al
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 .
drwxr-xr-x    4 root     root             0 Jan  1 00:00 ..
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 1000.rom -> ../../../devices/platform/soc/1000.rom
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 1700000.bus-error-unit -> ../../../devices/platform/soc/1700000.bus-error-unit
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 2000000.clint -> ../../../devices/platform/soc/2000000.clint
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20000000.serial -> ../../../devices/platform/soc/20000000.serial
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20010000.spi -> ../../../devices/platform/soc/20010000.spi
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 20040000.i2c -> ../../../devices/platform/soc/20040000.i2c
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 2010000.cache-controller -> ../../../devices/platform/soc/2010000.cache-controller
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 23000000.ethernet -> ../../../devices/platform/soc/23000000.ethernet
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 3000.error-device -> ../../../devices/platform/soc/3000.error-device
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 4000.teststatus -> ../../../devices/platform/soc/4000.teststatus
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 Fixed MDIO bus.0 -> ../../../devices/platform/Fixed MDIO bus.0
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 a000000.rom -> ../../../devices/platform/soc/a000000.rom
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 serial8250 -> ../../../devices/platform/serial8250
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc -> ../../../devices/platform/soc
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc:global-external-interrupts -> ../../../devices/platform/soc/soc:global-external-interrupts
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 soc:mdio -> ../../../devices/platform/soc/soc:mdio
# 
# ls /sys/bus/platform/devices/23000000.ethernet/subsystem/drivers -al
total 0
drwxr-xr-x   28 root     root             0 Jan  1 00:00 .
drwxr-xr-x    4 root     root             0 Jan  1 00:00 ..
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ahci
drwxr-xr-x    2 root     root             0 Jan  1 00:00 alarmtimer
drwxr-xr-x    2 root     root             0 Jan  1 00:00 dw-apb-uart
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ehci-platform
drwxr-xr-x    2 root     root             0 Jan  1 00:00 goldfish_rtc
drwxr-xr-x    2 root     root             0 Jan  1 00:00 gpio-clk
drwxr-xr-x    2 root     root             0 Jan  1 00:00 macb
drwxr-xr-x    2 root     root             0 Jan  1 00:00 of_fixed_clk
drwxr-xr-x    2 root     root             0 Jan  1 00:00 of_fixed_factor_clk
drwxr-xr-x    2 root     root             0 Jan  1 00:00 of_serial
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ohci-platform
drwxr-xr-x    2 root     root             0 Jan  1 00:00 pci-host-generic
drwxr-xr-x    2 root     root             0 Jan  1 00:00 pwrseq_emmc
drwxr-xr-x    2 root     root             0 Jan  1 00:00 pwrseq_simple
drwxr-xr-x    2 root     root             0 Jan  1 00:00 sdhci-cdns
drwxr-xr-x    2 root     root             0 Jan  1 00:00 serial8250
drwxr-xr-x    2 root     root             0 Jan  1 00:00 sifive-clk-prci
drwxr-xr-x    2 root     root             0 Jan  1 00:00 sifive-serial
drwxr-xr-x    2 root     root             0 Jan  1 00:00 sifive_gpio
drwxr-xr-x    2 root     root             0 Jan  1 00:00 sifive_spi
drwxr-xr-x    2 root     root             0 Jan  1 00:00 simple-reset
drwxr-xr-x    2 root     root             0 Jan  1 00:00 syscon
drwxr-xr-x    2 root     root             0 Jan  1 00:00 syscon-poweroff
drwxr-xr-x    2 root     root             0 Jan  1 00:00 syscon-reboot
drwxr-xr-x    2 root     root             0 Jan  1 00:00 xhci-hcd
drwxr-xr-x    2 root     root             0 Jan  1 00:00 xilinx-pcie
# 
# ls /sys/bus/m
mdio_bus/  mmc/       mmc_rpmb/
# ls /sys/bus/m
mdio_bus/  mmc/       mmc_rpmb/
# ls /sys/bus/mdio_bus/
devices            drivers_autoprobe  uevent
drivers            drivers_probe
# 
# ls /sys/bus/mdio_bus/devices/  空的
# 
# ls /sys/bus/mdio_bus/devices/ -al
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 .
drwxr-xr-x    4 root     root             0 Jan  1 00:00 ..
# 
# ls /sys/bus/mdio_bus/drivers/ -al
total 0
drwxr-xr-x   24 root     root             0 Jan  1 00:00 .
drwxr-xr-x    4 root     root             0 Jan  1 00:00 ..
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Generic Clause 45 PHY
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Generic PHY
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1101
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1111
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1111 (Finisar)
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1112
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1116R
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1118
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1121R
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1145
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1149R
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1240
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1318S
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1340S
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1510
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1540
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1545
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E1548P
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E3016
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E6341 Family
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E6390 Family
drwxr-xr-x    2 root     root             0 Jan  1 00:00 Marvell 88E6393 Family
# 
# 

drivers/net/ethernet/cadence/

static int macb_probe(struct platform_device *pdev)
{
        const struct macb_config *macb_config = &default_gem_config;
        int (*clk_init)(struct platform_device *, struct clk **,
                        struct clk **, struct clk **,  struct clk **,
                        struct clk **) = macb_config->clk_init;
        int (*init)(struct platform_device *) = macb_config->init;
        struct device_node *np = pdev->dev.of_node;
        struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
        struct clk *tsu_clk = NULL;
        unsigned int queue_mask, num_queues;
        bool native_io;
        phy_interface_t interface;
        struct net_device *dev;
        struct resource *regs;
        void __iomem *mem;
        struct macb *bp;
        int err, val;

        mem = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
       
void __iomem *
devm_platform_get_and_ioremap_resource(struct platform_device *pdev,
                unsigned int index, struct resource **res)
{
    struct resource *r;

    r = platform_get_resource(pdev, IORESOURCE_MEM, index);
    if (res)
        *res = r;
    return devm_ioremap_resource(&pdev->dev, r);
}
struct resource *platform_get_resource(struct platform_device *dev,
                                       unsigned int type, unsigned int num)
{
        u32 i;

        for (i = 0; i < dev->num_resources; i++) {
                struct resource *r = &dev->resource[i];

                if (type == resource_type(r) && num-- == 0)
                        return r;
        }
        return NULL;
}
/**
 * devm_ioremap_resource_wc() - write-combined variant of
 *                              devm_ioremap_resource()
 * @dev: generic device to handle the resource for
 * @res: resource to be handled
 *
 * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code
 * on failure.
 */
void __iomem *devm_ioremap_resource_wc(struct device *dev,
                                       const struct resource *res)
{
        return __devm_ioremap_resource(dev, res, DEVM_IOREMAP_WC);
}
static void __iomem *
__devm_ioremap_resource(struct device *dev, const struct resource *res,
                        enum devm_ioremap_type type)
{
        resource_size_t size;
        void __iomem *dest_ptr;
        char *pretty_name;

        BUG_ON(!dev);

        if (!res || resource_type(res) != IORESOURCE_MEM) {
                dev_err(dev, "invalid resource
");
                return IOMEM_ERR_PTR(-EINVAL);
        }

        if (type == DEVM_IOREMAP && res->flags & IORESOURCE_MEM_NONPOSTED)
                type = DEVM_IOREMAP_NP;

        size = resource_size(res);

        if (res->name)
                pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
                                             dev_name(dev), res->name);
        else
                pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
        if (!pretty_name) {
                dev_err(dev, "can't generate pretty name for resource %pR
", res);
                return IOMEM_ERR_PTR(-ENOMEM);
        }

        if (!devm_request_mem_region(dev, res->start, size, pretty_name)) {
                dev_err(dev, "can't request region for resource %pR
", res);
                return IOMEM_ERR_PTR(-EBUSY);
        }

        dest_ptr = __devm_ioremap(dev, res->start, size, type);
        if (!dest_ptr) {
                dev_err(dev, "ioremap failed for resource %pR
", res);
                devm_release_mem_region(dev, res->start, size);
                dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
        }

        return dest_ptr;
}

 内存映射devm_request_mem_region   devm_ioremap

devm_request_mem_region()申请出使用这段IO内存, 再使用ioremap() 将其映射出来, 供用户空间使用

其实说白了,request_mem_region函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。

重要的还是ioremap函数,ioremap主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换。

of_device_alloc

static inline unsigned long resource_type(const struct resource *res)
{
        return res->flags & IORESOURCE_TYPE_BITS;
}

static struct platform_device *of_platform_device_create_pdata(
                                        struct device_node *np,
                                        const char *bus_id,
                                        void *platform_data,
                                        struct device *parent)
{
        struct platform_device *dev;

        if (!of_device_is_available(np) ||
            of_node_test_and_set_flag(np, OF_POPULATED))
                return NULL;

        dev = of_device_alloc(np, bus_id, parent);
        if (!dev)
                goto err_clear_flag;

        dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
        if (!dev->dev.dma_mask)
                dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
        dev->dev.bus = &platform_bus_type;
        dev->dev.platform_data = platform_data;
        of_msi_configure(&dev->dev, dev->dev.of_node);

        if (of_device_add(dev) != 0) {
                platform_device_put(dev);
                goto err_clear_flag;
        }

        return dev;

err_clear_flag:
        of_node_clear_flag(np, OF_POPULATED);
        return NULL;
}

/**
 * of_device_alloc - Allocate and initialize an of_device
 * @np: device node to assign to device
 * @bus_id: Name to assign to the device.  May be null to use default name.
 * @parent: Parent device.
 */
struct platform_device *of_device_alloc(struct device_node *np,
                                  const char *bus_id,
                                  struct device *parent)
{
        struct platform_device *dev;
        int rc, i, num_reg = 0, num_irq;
        struct resource *res, temp_res;

        dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
        if (!dev)
                return NULL;

        /* count the io and irq resources */
        while (of_address_to_resource(np, num_reg, &temp_res) == 0)
                num_reg++;
        num_irq = of_irq_count(np);

        /* Populate the resource table */
        if (num_irq || num_reg) {
                res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);
                if (!res) {
                        platform_device_put(dev);
                        return NULL;
                }

                dev->num_resources = num_reg + num_irq;
                dev->resource = res;
                for (i = 0; i < num_reg; i++, res++) {
                        rc = of_address_to_resource(np, i, res);
                        WARN_ON(rc);
                }
                if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
                        pr_debug("not all legacy IRQ resources mapped for %pOFn
",
                                 np);
        }

        dev->dev.of_node = of_node_get(np);
        dev->dev.fwnode = &np->fwnode;
        dev->dev.parent = parent ? : &platform_bus;

        if (bus_id)
                dev_set_name(&dev->dev, "%s", bus_id);
        else
                of_device_make_bus_id(&dev->dev);

        return dev;
}


static int __of_address_to_resource(struct device_node *dev, int index, int bar_no,
                struct resource *r)
{
        u64 taddr;
        const __be32    *addrp;
        u64             size;
        unsigned int    flags;
        const char      *name = NULL;

        addrp = __of_get_address(dev, index, bar_no, &size, &flags);
        if (addrp == NULL)
                return -EINVAL;

        /* Get optional "reg-names" property to add a name to a resource */
        if (index >= 0)
                of_property_read_string_index(dev, "reg-names", index, &name);

        if (flags & IORESOURCE_MEM)
                taddr = of_translate_address(dev, addrp);
        else if (flags & IORESOURCE_IO)
                taddr = of_translate_ioport(dev, addrp, size);
        else
                return -EINVAL;

        if (taddr == OF_BAD_ADDR)
                return -EINVAL;
        memset(r, 0, sizeof(struct resource));

        if (of_mmio_is_nonposted(dev))
                flags |= IORESOURCE_MEM_NONPOSTED;

        r->start = taddr;
        r->end = taddr + size - 1;
        r->flags = flags;
        r->name = name ? name : dev->full_name;

        return 0;
}

原来是dts中reg配错了  __of_get_address(dev, index, bar_no, &size, &flags);返回的addrp == NULL
static int __of_address_to_resource(struct device_node *dev, int index, int bar_no,
                struct resource *r)
{
        u64 taddr;
        const __be32    *addrp;
        u64             size;
        unsigned int    flags;
        const char      *name = NULL;

        addrp = __of_get_address(dev, index, bar_no, &size, &flags);
        pr_err("tracing %s __of_get_address  %d" ,dev->full_name, addrp == NULL);
        if (addrp == NULL)
                return -EINVAL;

        /* Get optional "reg-names" property to add a name to a resource */
        if (index >= 0)
                of_property_read_string_index(dev, "reg-names", index, &name);

        if (flags & IORESOURCE_MEM)
                taddr = of_translate_address(dev, addrp);
        else if (flags & IORESOURCE_IO)
                taddr = of_translate_ioport(dev, addrp, size);
        else
                return -EINVAL;

        if (taddr == OF_BAD_ADDR)
                return -EINVAL;
        memset(r, 0, sizeof(struct resource));

        if (of_mmio_is_nonposted(dev))
                flags |= IORESOURCE_MEM_NONPOSTED;

        r->start = taddr;
        r->end = taddr + size - 1;
        r->flags = flags;
        r->name = name ? name : dev->full_name;

        return 0;
}
static struct of_bus of_busses[] = {
    //刪除了無關代碼
    /* Default */
    {
        .name = "default",
        .addresses = "reg",
        .match = NULL,
        .count_cells = of_bus_default_count_cells,
        .map = of_bus_default_map,
        .translate = of_bus_default_translate,
        .get_flags = of_bus_default_get_flags,
    },
};

用于获取地址相关属性。主要是“reg”和“assigned-addresser

const __be32 *of_get_address(struct device_node *dev,int index,u64 *size,unsigned int *flags)

dev:设备节点
index:要读取的地址标号。
size:地址长度
flags:参数,比如 IORESOURCE_IOIORESOURCE_MEM 等
返回值: 读取到的地址数据首地址,为 NULL 的话表示读取失败。

负责将从设备树读取到的地址转换为物理地址

u64 of_translate_address(struct device_node *dev,const __be32 *in_addr)

dev:设备节点。
in_addr:要转换的地址。
返回值: 得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。IIC、 SPI、 GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间, Linux内核使用 resource 结构体来描述一段内存空间

int of_address_to_resource(struct device_node *dev,int index,struct resource *r)

dev:设备节点。
index:地址资源标号。
r:得到的 resource 类型的资源值。
返回值: 0,成功;负值,失败。

用于直接内存映射.采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址,of_iomap 函数本质上也是将 reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过index参数指定要完成内存映射的是哪一段

void __iomem *of_iomap(struct device_node *np,int index)

np:设备节点。
index: reg 属性中要完成内存映射的段,如果 reg属性只有一段的话 index 就设置为 0
返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况, index0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap

static unsigned int of_bus_default_get_flags(const __be32 *addr)
{
    return IORESOURCE_MEM;
}
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
            unsigned int *flags)
{
    const __be32 *prop;
    unsigned int psize;
    struct device_node *parent;
    struct of_bus *bus;
    int onesize, i, na, ns;

    /* Get parent & match bus type */
    parent = of_get_parent(dev);
    if (parent == NULL)
        return NULL;
    bus = of_match_bus(parent);
    bus->count_cells(dev, &na, &ns);
    of_node_put(parent);
    if (!OF_CHECK_ADDR_COUNT(na))
        return NULL;

    /* Get "reg" or "assigned-addresses" property */
    prop = of_get_property(dev, bus->addresses, &psize);
    if (prop == NULL)
        return NULL;
    psize /= 4;

    onesize = na + ns;
    for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
        if (i == index) {
            if (size)
                *size = of_read_number(prop + na, ns);
            if (flags)
                *flags = bus->get_flags(prop);
            return prop;
        }
    return NULL;
}
EXPORT_SYMBOL(of_get_address);
/ {
        DTS_demo {
                compatible = "DTS_demo, BiscuitOS";
                status = "okay";
                reg = <0x11223344 0x55667788
                       0x10203040 0x50607080
                       0x99aabbcc 0xddeeff00
                       0x90a0b0c0 0xd0e0f000>;
        };
};

指的注意的是,对于 reg 属性的结构,其由:“值+地址”构成,且值在前,地址在后,即 当 reg 包含多个值的时候,如下:

/ {
        DTS_demo {
                compatible = "DTS_demo, BiscuitOS";
                status = "okay";
                reg = <0x11223344 0x55667788
                       0x10203040 0x50607080
                       0x99aabbcc 0xddeeff00
                       0x90a0b0c0 0xd0e0f000>;
        };
};

(假设 #address-cells 和 #size-cells 都是 2)

reg 第一个成员的值是: 0x1122334455667788

reg 第一个成员的地址是: 0x1020304050607080

reg 第二个成员的值是: 0x99aabbccddeeff00

reg 第二个成员的地址是: 0x90a0b0c0d0e0f000

所以 index 参数为 0 的时候,of_get_address() 函数返回 0x1020304050607080. 同理 index 参数为 1 的时候,of_get_address() 函数返回 0x90a0b0c0d0e0f000

static int DTS_demo_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    const u32 *regaddr_p;
    u64 addr;
    
    printk("DTS demo probe entence.
");

    /* get first address from 'reg' property */
    regaddr_p = of_get_address(np, 0, &addr, NULL);
    if (regaddr_p)
        printk("%s's address[0]: %#llx
", np->name, addr);

    /* get second address from 'reg' property */
    regaddr_p = of_get_address(np, 1, &addr, NULL);
    if (regaddr_p)
        printk("%s's address[1]: %#llx
", np->name, addr);

    return 0;
}
[    3.565634] DTS demo probe entence.
[    3.565648] DTS_demo's address[0]: 0x102030405060780
[    3.565660] DTS_demo's address[1]: 0x90a0b0c0d0e0f00

 resouces 初始化

arm: request_standard_resources(const struct machine_desc *mdesc)

riscv :  __init init_resources(void)

IRQ index 0 not found

int platform_get_irq(struct platform_device *dev, unsigned int num)
{
        int ret;

        ret = platform_get_irq_optional(dev, num);
        if (ret < 0 && ret != -EPROBE_DEFER)
                dev_err(&dev->dev, "IRQ index %u not found
", num);

        return ret;
}
 of_platform_populate()-->                                        //创建平台设备
        of_platform_bus_create()-->
            of_platform_device_create_pdata()-->
                of_device_alloc()-->
                    of_irq_to_resource_table()-->
                        of_irq_to_resource()-->
                            irq_of_parse_and_map()-->
                                irq_create_of_mapping()-->
                                    irq_create_mapping()-->
                                        irq_domain_associate()-->
                                            //即gic_irq_domain_map:里面初始化irq_desc->handle_irq
                                            domain->ops->map()    
platform_get_irq()->
    of_irq_get()->
        of_irq_parse_one()                  ## 解析dts中中斷相關屬性,填充結構體of_phandle_args中的args[]引數
        irq_create_of_mapping()->           
            of_phandle_args_to_fwspec()     ## 將of_phandle_args->args[]賦值給fwspec->param[],給translate使用
            irq_create_fwspec_mapping()->   
                irq_domain_translate()->    ## 獲取中斷號和中斷觸發型別
                    translate()->           ## 對應某個版本的gic處理函式
  platform_get_irq()-->
        of_irq_get()-->
            irq_create_of_mapping()-->
                irq_create_mapping(domain,hwirq)-->
                    irq_domain_associate()-->
                        //即gic_irq_domain_map:里面初始化irq_desc->handle_irq
                        domain->ops->map()    //gic_irq_domain_map

 

GICv3  中断控制器

drivers/irqchip/irq-gic-v3.c

translate()函式實現如下

static const struct irq_domain_ops gic_irq_domain_ops = {
    .translate = gic_irq_domain_translate,  ## .translate的實現函式
    ...
};
​
static int gic_irq_domain_translate(struct irq_domain *d,
                    struct irq_fwspec *fwspec,
                    unsigned long *hwirq,
                    unsigned int *type)
{
        ...
        switch (fwspec->param[0]) {
        case 0:         /* SPI */
            *hwirq = fwspec->param[1] + 32;  ## 中斷號
            break;
        case 1:         /* PPI */
            *hwirq = fwspec->param[1] + 16; ## 中斷號
            break;
        case GIC_IRQ_TYPE_LPI:  /* LPI */
            *hwirq = fwspec->param[1];     ## 中斷號
            break;
        default:
            return -EINVAL;
        }
        *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; ## 中斷觸發型別
        ...
}

應用舉例

RockPI 4A單板Debian系統Linux 4.4核心中的獲取HDMI中斷號為例。

1、查詢中斷號

從手冊“Rockchip RK3399 TRM V1.3 Part1.pdf”中,可以查到HDMI_IRQ中斷號,即55。

2、dts配置

檔案:arch/arm64/boot/dts/rockchip/rk3399.dtsi

    hdmi: hdmi@ff940000 {
        compatible = "rockchip,rk3399-dw-hdmi";
        ...
        interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH 0>;
        ...
    }

hdmi使用的是GIC_SPI中斷,按照gic_irq_domain_translate()函式中處理,需要將中斷號55減去32,得到dts中的中斷號23。

注:interrupts = <中斷型別 中斷號 中斷觸發型別 中斷分割槽(對應哪個CPU cluster,PPI型別中斷特有)>

3、驅動函式

檔案:driversgpudrm ockchipdw_hdmi-rockchip.c

 
 

static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
                 void *data)
{
    ...
    irq = platform_get_irq(pdev, 0);
    ...
}

此時,irq返回值為55。

後續會介紹GIC和中斷註冊等實現函式

RISCV中断控制器

 RISCV标准的中断控制器分为两部分内核中断CLINT(Core Local Interrupt)和外设中断控制器Platform-Level Interrupt Controller(PLIC),CLINT在每一个smp架构下每个core都各有自己私有的中断,PLIC则是所以core共用的外部中断控制器。

# cat /proc/interrupts 
           CPU0       
  1:      76094  SiFive PLIC   7  timer
  5:          1  RISC-V INTC   5  riscv-timer
 11:        188  SiFive PLIC   5  ttyS0
IPI0:         0  Rescheduling interrupts
IPI1:         0  Function call interrupts
IPI2:         0  CPU stop interrupts
IPI3:         0  IRQ work interrupts
IPI4:         0  Timer broadcast interrupts
drivers/irqchip/irq-sifive-plic.c:170:  .name           = "SiFive PLIC",
static struct irq_chip plic_chip = {
        .name           = "SiFive PLIC",
        .irq_mask       = plic_irq_mask,
        .irq_unmask     = plic_irq_unmask,
        .irq_eoi        = plic_irq_eoi,
#ifdef CONFIG_SMP
        .irq_set_affinity = plic_set_affinity,
#endif
};
# cat /sys/bus/clocksource/devices/clocksource0/available_clocksource 
riscv_clocksource arm,sp804 
# 
# echo 'arm,sp804' > /sys/bus/clocksource/devices/clocksource0/current_clocksour
ce 
[  975.375700] clocksource: Switched to clocksource arm,sp804
# 
# date
Thu Jan  1 00:12:39 UTC 1970
# 
# date
Thu Jan  1 00:12:39 UTC 1970
# 
# cat /proc/interrupts 
           CPU0       
  1:      80201  SiFive PLIC   7  timer
  5:          1  RISC-V INTC   5  riscv-timer
 11:        742  SiFive PLIC   5  ttyS0
IPI0:         0  Rescheduling interrupts
IPI1:         0  Function call interrupts
IPI2:         0  CPU stop interrupts
IPI3:         0  IRQ work interrupts
IPI4:         0  Timer broadcast interrupts
# 
# cat /proc/interrupts 
           CPU0       
  1:      80280  SiFive PLIC   7  timer
  5:          1  RISC-V INTC   5  riscv-timer
 11:        758  SiFive PLIC   5  ttyS0
IPI0:         0  Rescheduling interrupts
IPI1:         0  Function call interrupts
IPI2:         0  CPU stop interrupts
IPI3:         0  IRQ work interrupts
IPI4:         0  Timer broadcast interrupts
# 

drivers/irqchip/irq-riscv-intc.c:92:static const struct irq_domain_ops riscv_intc_domain_ops =

 
drivers/irqchip/irq-sifive-plic.c:212:static const struct irq_domain_ops plic_irqdomain_ops = {

static const struct irq_domain_ops plic_irqdomain_ops = {
        .translate      = irq_domain_translate_onecell,
        .alloc          = plic_irq_domain_alloc,
        .free           = irq_domain_free_irqs_top,
};

plic_chip

static struct irq_chip plic_chip = {
        .name           = "SiFive PLIC",
        .irq_mask       = plic_irq_mask,
        .irq_unmask     = plic_irq_unmask,
        .irq_eoi        = plic_irq_eoi,
#ifdef CONFIG_SMP
        .irq_set_affinity = plic_set_affinity,
#endif
};
plic_irq_domain_alloc 
-->plic_irqdomain_map
static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
                              irq_hw_number_t hwirq)
{
        struct plic_priv *priv = d->host_data;

        irq_domain_set_info(d, irq, hwirq, &plic_chip, d->host_data,
                            handle_fasteoi_irq, NULL, NULL);
        irq_set_noprobe(irq);
        irq_set_affinity(irq, &priv->lmask);
        return 0;
}
static const struct irq_domain_ops plic_irqdomain_ops = {
        .translate      = irq_domain_translate_onecell,
        .alloc          = plic_irq_domain_alloc,
        .free           = irq_domain_free_irqs_top,
};

plic_init

static int __init plic_init(struct device_node *node,
                struct device_node *parent)

priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
                        &plic_irqdomain_ops, priv);
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
plic_init -->
        irq_domain_add_linear(node, nr_irqs + 1,
                        &plic_irqdomain_ops, priv);  -->
                 plic_irq_domain_alloc-->
plic_irqdomain_map
-->
                           irq_domain_set_info(d, irq, hwirq, &plic_chip, d->host_data,
                            handle_fasteoi_irq, NULL, NULL);


plic_irqdomain_ops

[root@centos7 linux-5.14]# cat System.map  | grep plic_irqdomain_ops
ffffffff80e31bf8 d plic_irqdomain_ops
[root@centos7 linux-5.14]# cat System.map  | grep riscv_intc_domain_ops
ffffffff80e31b98 d riscv_intc_domain_ops
[root@centos7 linux-5.14]# 
#define IRQCHIP_DECLARE(name,compstr,fn)                
    static const struct of_device_id irqchip_of_match_##name    
    __used __section(__irqchip_of_table)                
    = { .compatible = compstr, .data = fn }
[root@centos7 linux-5.14]# cat vmlinux.dis  | grep irqchip_of_match
ffffffff8080db26:       2d757063                bgeu    a0,s7,ffffffff8080dde6 <irqchip_of_match_end+0xae>
ffffffff8080dd38 <irqchip_of_match_end>:
ffffffff8080e468:       b0cd                    j       ffffffff8080dd4a <irqchip_of_match_end+0x12>
ffffffff80891ea2:       ed17bd6f                jal     s10,ffffffff8080dd72 <irqchip_of_match_end+0x3a>
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */

网卡的interrupt-parent

irq_create_fwspec_mapping(传入domain和参数)
    irq_find_matching_fwspec
        domain->ops->match // 查看当前domain和参数指定的domain是否匹配
    irq_domain_translate
        domain->ops->translate // 通过参数申请一个hirq号
    irq_find_mapping // 通过hirq去查询,这个hirq是否已经映射,如果已经映射,就返回virq
    irq_domain_alloc_irqs // hirq没有映射,通过该函数去申请一个virq并映射
        __irq_domain_alloc_irqs
            irq_domain_alloc_descs // 申请一个irq_desc描述
            irq_domain_alloc_irq_data // 为virq设置irq_data树,该virq会为它所在的domain的父domain,一直到root domain添加irq_data
            irq_domain_alloc_irqs_hierarchy // 向domain申请hirq,并和virq做映射
                domain->ops->alloc
                    irq_domain_set_hwirq_and_chip // 将hirq与virq的irq_data关联
            irq_domain_insert_irq//将virq信息插入到每一级irq_data里面(一个domain对应一个irq_data)

of_irq_get

重点分析 of_irq_get,这个函数会获得device node,如pinctrl@11000000节点的interrupts属性的第index个中断的参数,
这是通过of_irq_parse_one完成的,然后获得该中断所隶属的interrupt-controller的irq domain,
利用该domain的of_xlate函数从前面表示第index个中断的参数中解析出hwirq和中断类型,最后从系统中为该hwriq分配一个全局唯一的virq,
并将映射关系存放到中断控制器的irq domain中,也就是gic的irq domain。 结合kernel代码分析: of_irq_get
->of_irq_parse_one -> irq_create_of_mapping --> irq_create_fwspec_mapping —>gic_irq_domain_translate
 dev_err(&pdev->dev, "IRQ num :%d, %d 
 ", platform_get_irq(pdev, 0), platform_get_irq(pdev, 1))
macb 20030000.ethernet: IRQ num :17, 18 
[   10.566230] irq: tracing :soc:interrupt-controller@c000000 call irq_find_mapping hwirq  12, virq  17 
[   10.586775] irq: tracing :soc:interrupt-controller@c000000 call irq_find_mapping hwirq  13, virq  18 

 irq_of_parse_and_map

of/irq.c

Unable to request IRQ

    queue->irq = platform_get_irq(pdev, q);
                err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt,
                                       IRQF_SHARED, dev->name, queue);
                if (err) {
                        dev_err(&pdev->dev,
                                "Unable to request IRQ %d (error %d)
",
                                queue->irq, err);
                        return err;
                }
eth0: ethernet@20030000{
                        compatible = "sifive,fu540-c000-gem";
                        interrupt-parent = <&L5>;
                        //interrupts = <61>, <62>;
                        interrupts = <7>, <8>;

硬件中断号:7

虚拟中断号:1

[    9.818234] irq: tracing :soc:interrupt-controller@c000000 call irq_domain_translate
[    9.829441] tracing irq_domain_translate_onecell!
[    9.836404] irq: tracing :soc:interrupt-controller@c000000 call irq_find_mapping hwirq  7, virq  1 
[    9.849554] genirq: Flags mismatch irq 1. 00000080 (eth%d) vs. 00015200 (timer)
[    9.860273] macb 20030000.ethernet: Unable to request IRQ 1 (error -16)
[    9.870009] macb 20030000.ethernet: tracing macb probe call  init -16.
[    9.880331] macb: probe of 20030000.ethernet failed with error -16
hwirq  7 申请了多次

 先把硬件中断号写死测试

#ifdef TEST
                queue->irq =  12;
#else
                queue->irq = platform_get_irq(pdev, q);
#endif

 

 

 

 

# cat /proc/interrupts 
           CPU0       
  1:      71178  SiFive PLIC   7  timer
  5:          1  RISC-V INTC   5  riscv-timer
 11:        363  SiFive PLIC   5  ttyS0
 12:          0  SiFive PLIC   6  eth0, eth0, eth0
IPI0:         0  Rescheduling interrupts
IPI1:         0  Function call interrupts
IPI2:         0  CPU stop interrupts
IPI3:         0  IRQ work interrupts
IPI4:         0  Timer broadcast interrupts
# 
# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop 
    link/ether 06:d8:8d:a9:ca:ab brd ff:ff:ff:ff:ff:ff
# 
# 

网卡注册终于成功了

# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop 
    link/ether 06:d8:8d:a9:ca:ab brd ff:ff:ff:ff:ff:ff
# 
# ls /sys/bus/platform/
devices/           drivers_autoprobe  uevent
drivers/           drivers_probe
# ls /sys/bus/platform/devices/
1000.rom                        3000.error-device
1700000.bus-error-unit          4000.teststatus
2000000.clint                   Fixed MDIO bus.0
20000000.serial                 a000000.rom
20010000.spi                    serial8250
20030000.ethernet               soc
20040000.i2c                    soc:global-external-interrupts
2010000.cache-controller
# 
# ls /sys/bus/platform/devices/20030000.ethernet  -al
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 /sys/bus/platform/devices/20030000.ethernet -> ../../../devices/platform/soc/20030000.ethernet
# 
# ls /sys/bus/platform/devices/20030000.ethernet/driver  -al
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 /sys/bus/platform/devices/20030000.ethernet/driver -> ../../../../bus/platform/drivers/macb
# 
# 

中断的映射

当早期的系统只存在一个中断控制器,而且中断数目也不多的时候,一个很简单的做法就是一个中断号对应到中断控制器的一个号,可以说是简单的线性映射:


但当一个系统中有多个中断控制器,而且中断号也逐渐增加的时候。linux 内核为了应对此问题,引入了 irq_domain 的概念。


irq_domain 的引入相当于一个中断控制器就是一个 irq_domain。这样一来所有的中断控制器就会出现级联的布局。利用树状的结构可以充分的利用 irq 数目,而且每一个 irq_domain 区域可以自己去管理自己 interrupt 的特性。

每一个中断控制器对应多个中断号, 而硬件中断号在不同的中断控制器上是会重复编码的, 这时仅仅用硬中断号已经不能唯一标识一个外设中断,因此 linux kernel 提供了一个虚拟中断号的概念。

接下来我们看下硬件中断号是如何映射到虚拟中断号的。
 

数据结构

在看硬件中断号映射到虚拟中断号之前,先来看下几个比较重要的数据结构。

struct irq_desc 描述一个外设的中断,称之中断描述符。

struct irq_desc {
 struct irq_common_data irq_common_data;
 struct irq_data  irq_data;  
 unsigned int __percpu *kstat_irqs;
 irq_flow_handler_t handle_irq;  
  ......
 struct irqaction *action; 
 ......
} ____cacheline_internodealigned_in_smp;
 
  • irq_data:中断控制器的硬件数据

  • handle_irq:中断控制器驱动的处理函数,指向一个 struct irqaction 的链表,一个中断源可以多个设备共享,所以一个 irq_desc 可以挂载多个 action,由链表结构组织起来

  • action:设备驱动的处理函数

 struct irq_data 包含中断控制器的硬件数据。

struct irq_data {
 u32   mask;
 unsigned int  irq;
 unsigned long  hwirq;
 struct irq_common_data *common;
 struct irq_chip  *chip;
 struct irq_domain *domain;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 struct irq_data  *parent_data;
#endif
 void   *chip_data;
};
 
  • irq:虚拟中断号

  • hwirq:硬件中断号

  • chip:对应的 irq_chip 数据结构

  • domain:对应的 irq_domain 数据结构

struct irq_chip 用于对中断控制器的硬件操作。

struct irq_chip {
 struct device *parent_device;
 const char *name;
 unsigned int (*irq_startup)(struct irq_data *data);
 void  (*irq_shutdown)(struct irq_data *data);
 void  (*irq_enable)(struct irq_data *data);
 void  (*irq_disable)(struct irq_data *data);
 
 void  (*irq_ack)(struct irq_data *data);
 void  (*irq_mask)(struct irq_data *data);
 void  (*irq_mask_ack)(struct irq_data *data);
 void  (*irq_unmask)(struct irq_data *data);
 void  (*irq_eoi)(struct irq_data *data);
 
 int  (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
 int  (*irq_retrigger)(struct irq_data *data);
 int  (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
 int  (*irq_set_wake)(struct irq_data *data, unsigned int on);
 
 void  (*irq_bus_lock)(struct irq_data *data);
 void  (*irq_bus_sync_unlock)(struct irq_data *data);
 ......

 
  • parent_device:指向父设备
  • name:/proc/interrupts 中显示的名字
  • irq_startup:启动中断,如果设置成 NULL,则默认为 enable
  • irq_shutdown:关闭中断,如果设置成 NULL,则默认为 disable
  • irq_enable:中断使能,如果设置成 NULL,则默认为 chip->unmask
  • irq_disable:中断禁止
  • irq_ack:开始新的中断
  • irq_mask:中断源屏蔽
  • irq_mask_ack:应答并屏蔽中断
  • irq_unmask:解除中断屏蔽
  • irq_eoi:中断处理结束后调用
  • irq_set_affinity:在 SMP 中设置 CPU 亲和力
  • irq_retrigger:重新发送中断到 CPU
  • irq_set_type:设置中断触发类型
  • irq_set_wake:使能/禁止电源管理中的唤醒功能
  • irq_bus_lock:慢速芯片总线上的锁
  • irq_bus_sync_unlock:同步释放慢速总线芯片的锁

 struct irq_domain 与中断控制器对应,完成硬件中断号 hwirq 到 virq 的映射。

struct irq_domain {
 struct list_head link;
 const char *name;
 const struct irq_domain_ops *ops;
 void *host_data;
 unsigned int flags;
 unsigned int mapcount;
 
 /* Optional data */
 struct fwnode_handle *fwnode;
 enum irq_domain_bus_token bus_token;
 struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 struct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
 struct dentry  *debugfs_file;
#endif
 
 /* reverse map data. The linear map gets appended to the irq_domain */
 irq_hw_number_t hwirq_max;
 unsigned int revmap_direct_max_irq;
 unsigned int revmap_size;
 struct radix_tree_root revmap_tree;
 unsigned int linear_revmap[];
};
 
  • link:用于将 irq_domain 连接到全局链表 irq_domain_list 中
  • name:irq_domain 的名称
  • ops:irq_domain 映射操作函数集
  • mapcount:映射好的中断的数量
  • fwnode:对应中断控制器的 device node
  • parent:指向父级 irq_domain 的指针,用于支持级联 irq_domain
  • hwirq_max:该 irq_domain 支持的中断最大数量
  • linear_revmap[]:hwirq->virq 反向映射的线性表

struct irq_domain_ops 是 irq_domain 映射操作函数集。

struct irq_domain_ops {
int (*match)(struct irq_domain *d, struct device_node *node,
enum irq_domain_bus_token bus_token);
int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);
int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
void (*unmap)(struct irq_domain *d, unsigned int virq);
int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);
......
};


match:用于中断控制器设备与 irq_domain 的匹配

map:用于硬件中断号与 Linux 中断号的映射

xlate:通过 device_node,解析硬件中断号和触发方式

struct irqaction 主要是用来存设备驱动注册的中断处理函数。

struct irqaction {
irq_handler_t handler;
void *dev_id;
......
unsigned int irq;
unsigned int flags;
......
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;


handler:设备驱动里的中断处理函数

dev_id:设备 id

irq:中断号

flags:中断标志,注册时设置,比如上升沿中断,下降沿中断等

name:中断名称,产生中断的硬件的名字

dir:指向 /proc/irq/ 相关的信息

这里,我们用一张图来汇总下上面的数据结构:
 

上面的结构体 struct irq_desc 是设备驱动加载的过程中完成的,让设备树中的中断能与具体的中断描述符 irq_desc 匹配,其中 struct irqaction 保存着设备的中断处理函数。右边框内的结构体主要是在中断控制器驱动加载的过程中完成的,其中 struct irq_chip 用于对中断控制器的硬件操作,struct irq_domain 用于硬件中断号到 Linux irq 的映射。

下面我们结合代码看下中断控制器驱动和设备驱动是如何创建这些结构体,并且硬中断和虚拟中断号是如何完成映射的。
 

中断控制器注册 irq_domain

通过 __irq_domain_add 初始化 irq_domain 数据结构,然后把 irq_domain 添加到全局的链表 irq_domain_list 中。

外设的驱动创建硬中断和虚拟中断号的映射关系

设备的驱动在初始化的时候可以调用 irq_of_parse_and_map 这个接口函数进行该 device node 中和中断相关的内容的解析,并建立映射关系


of_irq_parse_one 函数用于解析DTS文件中设备定义的属性,如"reg", “interrupt”

irq_find_matching_fwspec 遍历 irq_domain_list 链表,找到 device node 匹配的 irq_domain

gic_irq_domain_translate 解析出中断信息,比如硬件中断号 hwirq,中断触发方式

irq_domain_alloc_descs 分配一个虚拟的中断号 virq,分配和初始化中断描述符 irq_desc

gic_irq_domain_alloc 为 hwirq 和 virq 创建映射关系。内部会通过 irq_domain_set_info 调用 irq_domain_set_hwirq_and_chip,然后通过 virq 获取 irq_data 结构体,并将 hwirq 设置到 irq_data->hwirq 中, 最终完成 hwirq 到 virq 的映射

irq_domain_set_info 根据硬件中断号的范围设置 irq_desc->handle_irq 的指针,共享中断入口为 handle_fasteoi_irq,私有中断入口为 handle_percpu_devid_irq

最后,我们可以通过 /proc/interrupts 下的值来看下它们的关系:
 

现在,我们已经知道内核为硬件中断号与 Linux 中断号做了映射,相关数据结构的绑定及初始化,并且设置了中断处理函数执行的入口。接下来我们再看下设备的中断是怎么来注册的?

中断的注册
设备驱动中,获取到了 irq 中断号后,通常就会采用 request_irq/request_threaded_irq 来注册中断,其中 request_irq 用于注册普通处理的中断。request_threaded_irq 用于注册线程化处理的中断,线程化中断的主要目的把中断上下文的任务迁移到线程中,减少系统关中断的时间,增强系统的实时性。
————————————————
 

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
        const char *name, void *dev)
{
    return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

其中 irq 是 linux 中断号,handler 是中断处理函数,flags 是中断标志位,name 是中断的名字。在讲具体的注册流程前,先看一下主要的中断标志位:

#define IRQF_SHARED  0x00000080              //多个设备共享一个中断号,需要外设硬件支持
#define IRQF_PROBE_SHARED 0x00000100              //中断处理程序允许sharing mismatch发生
#define __IRQF_TIMER  0x00000200               //时钟中断
#define IRQF_PERCPU  0x00000400               //属于特定CPU的中断
#define IRQF_NOBALANCING 0x00000800               //禁止在CPU之间进行中断均衡处理
#define IRQF_IRQPOLL  0x00001000              //中断被用作轮训
#define IRQF_ONESHOT  0x00002000              //一次性触发的中断,不能嵌套,1)在硬件中断处理完成后才能打开中断;2)在中断线程化中保持关闭状态,直到该中断源上的所有thread_fn函数都执行完
#define IRQF_NO_SUSPEND  0x00004000      //系统休眠唤醒操作中,不关闭该中断
#define IRQF_FORCE_RESUME 0x00008000              //系统唤醒过程中必须强制打开该中断
#define IRQF_NO_THREAD  0x00010000      //禁止中断线程化
#define IRQF_EARLY_RESUME 0x00020000      //系统唤醒过程中在syscore阶段resume,而不用等到设备resume阶段
#define IRQF_COND_SUSPEND 0x00040000      //与NO_SUSPEND的用户共享中断时,执行本设备的中断处理函数
————————————————
 

 创建完成后,通过 ps 命令可以查看系统中的中断线程,注意这些线程是实时线程 SCHED_FIFO:

 ps -A | grep "irq/"
root          1749     2       0      0 irq_thread          0 S [irq/433-imx_drm]
root          1750     2       0      0 irq_thread          0 S [irq/439-imx_drm]
root          1751     2       0      0 irq_thread          0 S [irq/445-imx_drm]
root          1752     2       0      0 irq_thread          0 S [irq/451-imx_drm]
root          2044     2       0      0 irq_thread          0 S [irq/279-isl2902]
root          2192     2       0      0 irq_thread          0 S [irq/114-mmc0]
root          2199     2       0      0 irq_thread          0 S [irq/115-mmc1]
root          2203     2       0      0 irq_thread          0 S [irq/322-5b02000]
root          2361     2       0      0 irq_thread          0 S [irq/294-4-0051]

fu540_c000_config

static const struct macb_config fu540_c000_config = {
        .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO |
                MACB_CAPS_GEM_HAS_PTP,
        .dma_burst_length = 16,
        .clk_init = fu540_c000_clk_init,
        .init = fu540_c000_init,
        .jumbo_max_len = 10240,
        .usrio = &macb_default_usrio,
};

phy-handle

static bool macb_phy_handle_exists(struct device_node *dn)
{
        dn = of_parse_phandle(dn, "phy-handle", 0);
        of_node_put(dn);
        return dn != NULL;
}

macb_mii_init

macb_probe
    macb_mii_init
        mdiobus_register
            device_register
            mdiobus_scan 0~32
                get_phy_device
                    get_phy_id  //获取硬件PHY的ID,用来匹配驱动
                    phy_device_create
                        phy_bus_match //匹配驱动函数
            mdiobus_setup_mdiodev_from_board_info
            mdiobus_create_device
struct macb *bp;
dev = alloc_etherdev_mq(sizeof(*bp), num_queues);
    if (!dev) {
        err = -ENOMEM;
        goto err_disable_clocks;
    }

    dev->base_addr = regs->start;

    SET_NETDEV_DEV(dev, &pdev->dev);

    bp = netdev_priv(dev);
    bp->pdev = pdev;
    bp->dev = dev;
    bp->regs = mem;
    bp->native_io = native_io;
    if (native_io) {
        bp->macb_reg_readl = hw_readl_native;
        bp->macb_reg_writel = hw_writel_native;
    } else {
        bp->macb_reg_readl = hw_readl;
        bp->macb_reg_writel = hw_writel;
    }
    bp->num_queues = num_queues;
    bp->queue_mask = queue_mask;
    if (macb_config)
        bp->dma_burst_length = macb_config->dma_burst_length;
    bp->pclk = pclk;
    bp->hclk = hclk;
    bp->tx_clk = tx_clk;
    bp->rx_clk = rx_clk;
    bp->tsu_clk = tsu_clk;
    if (macb_config)
        bp->jumbo_max_len = macb_config->jumbo_max_len;

    bp->wol = 0;
    if (of_get_property(np, "magic-packet", NULL))
        bp->wol |= MACB_WOL_HAS_MAGIC_PACKET;
    device_set_wakeup_capable(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET);

    bp->usrio = macb_config->usrio;

    spin_lock_init(&bp->lock);

    /* setup capabilities */
    macb_configure_caps(bp, macb_config);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
    if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) {
        dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
        bp->hw_dma_cap |= HW_DMA_CAP_64B;
    }
#endif
    platform_set_drvdata(pdev, dev);

    dev->irq = platform_get_irq(pdev, 0);
    if (dev->irq < 0) {
        err = dev->irq;
        goto err_out_free_netdev;
    }

    /* MTU range: 68 - 1500 or 10240 */
    dev->min_mtu = GEM_MTU_MIN_SIZE;
    if (bp->caps & MACB_CAPS_JUMBO)
        dev->max_mtu = gem_readl(bp, JML) - ETH_HLEN - ETH_FCS_LEN;
    else
        dev->max_mtu = ETH_DATA_LEN;

    if (bp->caps & MACB_CAPS_BD_RD_PREFETCH) {
        val = GEM_BFEXT(RXBD_RDBUFF, gem_readl(bp, DCFG10));
        if (val)
            bp->rx_bd_rd_prefetch = (2 << (val - 1)) *
                        macb_dma_desc_get_size(bp);

        val = GEM_BFEXT(TXBD_RDBUFF, gem_readl(bp, DCFG10));
        if (val)
            bp->tx_bd_rd_prefetch = (2 << (val - 1)) *
                        macb_dma_desc_get_size(bp);
    }

    bp->rx_intr_mask = MACB_RX_INT_FLAGS;
    if (bp->caps & MACB_CAPS_NEEDS_RSTONUBR)
        bp->rx_intr_mask |= MACB_BIT(RXUBR);

    err = of_get_ethdev_address(np, bp->dev);
    if (err == -EPROBE_DEFER)
        goto err_out_free_netdev;
    else if (err)
        macb_get_hwaddr(bp);

    err = of_get_phy_mode(np, &interface);
    if (err)
        /* not found in DT, MII by default */
        bp->phy_interface = PHY_INTERFACE_MODE_MII;
    else
        bp->phy_interface = interface;

    /* IP specific init */
    err = init(pdev);
    if (err)
        goto err_out_free_netdev;

    err = macb_mii_init(bp);

 riscv64-unknown-elf-objdump -D vmlinux > vmlinux.dis

[    8.159921] tracing of_mdiobus_register 
[    8.177915] libphy: MACB_mii_bus: probed
[    8.184161] mdio_bus 20030000.ethernet-ffffffff: phy@43080000 has invalid PHY address
[    8.195607] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 0
[    8.207509] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 1
[    8.220358] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 2
[    8.232383] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 3
[    8.244580] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 4
[    9.087652] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 5
[    9.099650] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 6
[    9.111859] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 7
[    9.123743] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 8
[    9.135676] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 9
[    9.147697] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 10
[    9.159905] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 11
[    9.172822] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 12
[    9.184820] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 13
[    9.197081] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 14
[    9.209958] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 15
[    9.221969] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 16
[    9.234238] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 17
[    9.247078] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 18
[    9.259113] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 19
[    9.271320] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 20
[    9.283159] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 21
[    9.295137] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 22
[    9.307174] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 23
[    9.319369] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 24
[    9.332282] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 25
[    9.344286] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 26
[    9.356514] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 27
[    9.368358] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 28
[    9.380350] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 29
[    9.392378] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 30
[    9.404941] mdio_bus 20030000.ethernet-ffffffff: scan phy phy at address 31
[    9.417690] macb 20030000.ethernet: tracing macb_mii_init 0.
[    9.426973] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[    9.439705] Oops [#1]
[    9.443051] Modules linked in:
[    9.447465] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.0 #49
[    9.455869] Hardware name: SiFive,FU500 (DT)
[    9.461840] epc : register_netdevice+0xde/0x426
[    9.468367]  ra : register_netdevice+0xc2/0x426
[    9.474830] epc : ffffffff805386e0 ra : ffffffff805386c4 sp : ffffffd00400bb10
[    9.484863]  gp : ffffffff812d2f90 tp : ffffffe001650000 t0 : 0000000000000018
[    9.494891]  t1 : ffffffd00400ba28 t2 : 0000000000000001 s0 : ffffffd00400bb60
[    9.504916]  s1 : ffffffe002314000 a0 : ffffffe0023e0540 a1 : ffffffe0023e0540
[    9.514943]  a2 : ffffffe007c2d7a8 a3 : 0000000000000002 a4 : 0000000000000000
[    9.524943]  a5 : ffffffe0023e0550 a6 : 0000000000000030 a7 : ffffffffffffffff
[    9.534958]  s2 : 0000000000000000 s3 : ffffffff812baa00 s4 : ffffffe002316000
[    9.544975]  s5 : ffffffe007dfd330 s6 : ffffffe001700c10 s7 : 00000000000000ac
[    9.554987]  s8 : 0000000000000001 s9 : ffffffe002314780 s10: 0000000000000007
[    9.565006]  s11: 0000000000000003 t3 : 0000000000000003 t4 : ffffffd00400ba08
[    9.575022]  t5 : 0000000000000008 t6 : 00000001306fe0a0
[    9.582425] status: 0000000200000120 badaddr: 0000000000000000 cause: 000000000000000d
[    9.593378] [<ffffffff805386e0>] register_netdevice+0xde/0x426
[    9.601636] [<ffffffff80538a40>] register_netdev+0x18/0x2e
[    9.609409] [<ffffffff80474936>] macb_probe+0x5ec/0x7ea
[    9.616904] [<ffffffff80412298>] platform_probe+0x42/0x80
[    9.624599] [<ffffffff8057b57e>] really_probe+0xd0/0x24c   --------->call_driver_probe
[    9.632167] [<ffffffff8057b766>] __driver_probe_device.part.0+0x6c/0x7c
[    9.641501] [<ffffffff804105b0>] driver_probe_device+0x78/0xc4
[    9.649743] [<ffffffff80410b40>] __driver_attach+0x60/0x102
[    9.657635] [<ffffffff8040e606>] bus_for_each_dev+0x52/0x90  -----遍历设备
[    9.665505] [<ffffffff8040fec6>] driver_attach+0x1a/0x22
[    9.673029] [<ffffffff8040f9d4>] bus_add_driver+0xe6/0x198
[    9.680788] [<ffffffff8041119a>] driver_register+0x50/0xe4
[    9.688564] [<ffffffff8041201c>] __platform_driver_register+0x1c/0x24
[    9.697677] [<ffffffff8061bbee>] macb_driver_init+0x1a/0x22
[    9.705551] [<ffffffff800020da>] do_one_initcall+0x36/0x15e
[    9.713415] [<ffffffff80600fea>] kernel_init_freeable+0x1b0/0x214
[    9.722042] [<ffffffff8057f28e>] kernel_init+0x1e/0x104
[    9.729493] [<ffffffff80003010>] ret_from_exception+0x0/0xc
[    9.738222] ---[ end trace ffb13a3cb89d56af ]---
[    9.745444] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    9.756227] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---

 

ffffffff805386d8: f504 sd s1,40(a0)
ffffffff805386da: e91c sd a5,16(a0)
ffffffff805386dc: ed1c sd a5,24(a0)
ffffffff805386de: e888 sd a0,16(s1)
ffffffff805386e0: 631c ld a5,0(a4)
ffffffff805386e2: c791 beqz a5,ffffffff805386ee <register_netdevice+0xec>
ffffffff805386e4: 8526 mv a0,s1
ffffffff805386e6: 9782 jalr a5
ffffffff805386e8: 892a mv s2,a0
ffffffff805386ea: 1c051263 bnez a0,ffffffff805388ae <register_netdevice+0x2ac>
ffffffff805386ee: 74e8 ld a0,232(s1)
ffffffff805386f0: 70ec ld a1,224(s1)

 

[root@centos7 linux-5.14]# cat vmlinux.dis  | grep "register_netdevice>" -A 20
ffffffff80538602 <register_netdevice>:
ffffffff80538602:       715d                    addi    sp,sp,-80
ffffffff80538604:       e0a2                    sd      s0,64(sp)
ffffffff80538606:       e486                    sd      ra,72(sp)
ffffffff80538608:       fc26                    sd      s1,56(sp)
ffffffff8053860a:       f84a                    sd      s2,48(sp)
ffffffff8053860c:       f44e                    sd      s3,40(sp)
ffffffff8053860e:       0880                    addi    s0,sp,80
ffffffff80538610:       43023783                ld      a5,1072(tp) # 430 <__efistub_.L0 +0x1>
ffffffff80538614:       fcf43423                sd      a5,-56(s0)
ffffffff80538618:       4781                    li      a5,0
ffffffff8053861a:       3d81a783                lw      a5,984(gp) # ffffffff812d3368 <dev_boot_phase>
ffffffff8053861e:       43853983                ld      s3,1080(a0)
ffffffff80538622:       22079063                bnez    a5,ffffffff80538842 <register_netdevice+0x240>
ffffffff80538626:       84aa                    mv      s1,a0
ffffffff80538628:       0e2080ef                jal     ra,ffffffff8054070a <rtnl_is_locked>
ffffffff8053862c:       20050c63                beqz    a0,ffffffff80538844 <register_netdevice+0x242>
ffffffff80538630:       658d                    lui     a1,0x3
ffffffff80538632:       4601                    li      a2,0
ffffffff80538634:       80f58593                addi    a1,a1,-2033 # 280f <__efistub_.L0 +0xb>
ffffffff80538638:       00a57517                auipc   a0,0xa57

FFFFFFF80538700 = ffffffff80538602 + de

riscv64-unknown-elf-addr2line -C -f -e vmlinux FFFFFFFF80538700
register_netdevice
 linux-5.14/net/core/dev.c:10288

 

riscv64-unknown-elf-objdump  -S -D vmlinux  > vmlinux.txt
riscv64-unknown-elf-objdump: Warning: source file  linux-5.14/drivers/net/mdio/of_mdio.c is more recent than object file

原来是NULL == dev->netdev_ops,没用调用macb_init

static int macb_init(struct platform_device *pdev)
{
    struct net_device *dev = platform_get_drvdata(pdev);
    unsigned int hw_q, q;
    struct macb *bp = netdev_priv(dev);
    struct macb_queue *queue;
    int err;
    u32 val, reg;

    bp->tx_ring_size = DEFAULT_TX_RING_SIZE;
    bp->rx_ring_size = DEFAULT_RX_RING_SIZE;

    /* set the queue register mapping once for all: queue0 has a special
     * register mapping but we don't want to test the queue index then
     * compute the corresponding register offset at run time.
     */
    for (hw_q = 0, q = 0; hw_q < MACB_MAX_QUEUES; ++hw_q) {
        if (!(bp->queue_mask & (1 << hw_q)))
            continue;

        queue = &bp->queues[q];
        queue->bp = bp;
        netif_napi_add(dev, &queue->napi, macb_poll, NAPI_POLL_WEIGHT);
        if (hw_q) {
            queue->ISR  = GEM_ISR(hw_q - 1);
            queue->IER  = GEM_IER(hw_q - 1);
            queue->IDR  = GEM_IDR(hw_q - 1);
            queue->IMR  = GEM_IMR(hw_q - 1);
            queue->TBQP = GEM_TBQP(hw_q - 1);
            queue->RBQP = GEM_RBQP(hw_q - 1);
            queue->RBQS = GEM_RBQS(hw_q - 1);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
            if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
                queue->TBQPH = GEM_TBQPH(hw_q - 1);
                queue->RBQPH = GEM_RBQPH(hw_q - 1);
            }
#endif
        } else {
            /* queue0 uses legacy registers */
            queue->ISR  = MACB_ISR;
            queue->IER  = MACB_IER;
            queue->IDR  = MACB_IDR;
            queue->IMR  = MACB_IMR;
            queue->TBQP = MACB_TBQP;
            queue->RBQP = MACB_RBQP;
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
            if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
                queue->TBQPH = MACB_TBQPH;
                queue->RBQPH = MACB_RBQPH;
            }
#endif
        }

        /* get irq: here we use the linux queue index, not the hardware
         * queue index. the queue irq definitions in the device tree
         * must remove the optional gaps that could exist in the
         * hardware queue mask.
         */
        queue->irq = platform_get_irq(pdev, q);
        err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt,
                       IRQF_SHARED, dev->name, queue);
        if (err) {
            dev_err(&pdev->dev,
                "Unable to request IRQ %d (error %d)
",
                queue->irq, err);
            return err;
        }

        INIT_WORK(&queue->tx_error_task, macb_tx_error_task);
        q++;
    }

    dev->netdev_ops = &macb_netdev_ops;

has invalid PHY address

static inline int of_mdio_parse_addr(struct device *dev,
                                     const struct device_node *np)
{
        u32 addr;
        int ret;

        ret = of_property_read_u32(np, "reg", &addr);
        if (ret < 0) {
                dev_err(dev, "%s has invalid PHY address
", np->full_name);
                return ret;
        }

        /* A PHY must have a reg property in the range [0-31] */
        if (addr >= PHY_MAX_ADDR) {
                dev_err(dev, "%s PHY address %i is too large
",
                        np->full_name, addr);
                return -EINVAL;
        }

        return addr;
}
int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
{
    struct device_node *child;
    bool scanphys = false;
    int addr, rc;
    if (!np)
        return mdiobus_register(mdio);
    /* Do not continue if the node is disabled */
    if (!of_device_is_available(np))
        return -ENODEV;
    /* Mask out all PHYs from auto probing.  Instead the PHYs listed in
     * the device tree are populated after the bus has been registered */
    mdio->phy_mask = ~0;
    mdio->dev.of_node = np;
    mdio->dev.fwnode = of_fwnode_handle(np);
    /* Get bus level PHY reset GPIO details */
    mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY;
    of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us);
    /* Register the MDIO bus */
    rc = mdiobus_register(mdio);
    if (rc)
        return rc;
    /* Loop over the child nodes and register a phy_device for each phy */
    for_each_available_child_of_node(np, child) {
        addr = of_mdio_parse_addr(&mdio->dev, child);
        if (addr < 0) {
            scanphys = true;
            continue;
        }
        if (of_mdiobus_child_is_phy(child))
            rc = of_mdiobus_register_phy(mdio, child, addr);
        else
            rc = of_mdiobus_register_device(mdio, child, addr);
        if (rc == -ENODEV)
            dev_err(&mdio->dev,
                "MDIO device at address %d is missing.
",
                addr);
        else if (rc)
            goto unregister;
    }
    if (!scanphys)
        return 0;
    /* auto scan for PHYs with empty reg property */
    for_each_available_child_of_node(np, child) {
        /* Skip PHYs with reg property set */
        if (of_find_property(child, "reg", NULL))
            continue;
        for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
            /* skip already registered PHYs */
            if (mdiobus_is_registered_device(mdio, addr))
                continue;
            /* be noisy to encourage people to set reg property */
            dev_info(&mdio->dev, "scan phy %pOFn at address %i
",
                 child, addr);
            if (of_mdiobus_child_is_phy(child)) {
                rc = of_mdiobus_register_phy(mdio, child, addr);
                if (rc && rc != -ENODEV)
                    goto unregister;
            }
        }
    }
    return 0;
unregister:
    mdiobus_unregister(mdio);
    return rc;
}
EXPORT_SYMBOL(of_mdiobus_register);

scan phy phy at address

[    8.207509] mdio_bus  ethernet-ffffffff: scan phy phy at address 1
[    8.220358] mdio_bus  ethernet-ffffffff: scan phy phy at address 2
[    8.232383] mdio_bus  ethernet-ffffffff: scan phy phy at address 3
[    8.244580] mdio_bus  ethernet-ffffffff: scan phy phy at address 4
[    9.087652] mdio_bus  ethernet-ffffffff: scan phy phy at address 5
[    9.099650] mdio_bus  ethernet-ffffffff: scan phy phy at address 6
[    8.195607] mdio_bus  ethernet-ffffffff: scan phy phy at address 0
[    9.111859] mdio_bus  ethernet-ffffffff: scan phy phy at address 7
[    9.123743] mdio_bus  ethernet-ffffffff: scan phy phy at address 8
[    9.135676] mdio_bus  ethernet-ffffffff: scan phy phy at address 9
[    9.147697] mdio_bus  ethernet-ffffffff: scan phy phy at address 10
[    9.159905] mdio_bus  ethernet-ffffffff: scan phy phy at address 11
[    9.172822] mdio_bus  ethernet-ffffffff: scan phy phy at address 12
[    9.184820] mdio_bus  ethernet-ffffffff: scan phy phy at address 13
[    9.197081] mdio_bus  ethernet-ffffffff: scan phy phy at address 14
[    9.209958] mdio_bus  ethernet-ffffffff: scan phy phy at address 15
[    9.221969] mdio_bus  ethernet-ffffffff: scan phy phy at address 16
[    9.234238] mdio_bus  ethernet-ffffffff: scan phy phy at address 17
[    9.247078] mdio_bus  ethernet-ffffffff: scan phy phy at address 18
[    9.259113] mdio_bus  ethernet-ffffffff: scan phy phy at address 19
[    9.271320] mdio_bus  ethernet-ffffffff: scan phy phy at address 20
[    9.283159] mdio_bus  ethernet-ffffffff: scan phy phy at address 21
[    9.295137] mdio_bus  ethernet-ffffffff: scan phy phy at address 22
[    9.307174] mdio_bus  ethernet-ffffffff: scan phy phy at address 23
[    9.319369] mdio_bus  ethernet-ffffffff: scan phy phy at address 24
[    9.332282] mdio_bus  ethernet-ffffffff: scan phy phy at address 25
[    9.344286] mdio_bus  ethernet-ffffffff: scan phy phy at address 26
[    9.356514] mdio_bus  ethernet-ffffffff: scan phy phy at address 27
[    9.368358] mdio_bus  ethernet-ffffffff: scan phy phy at address 28
[    9.380350] mdio_bus  ethernet-ffffffff: scan phy phy at address 29
[    9.392378] mdio_bus  ethernet-ffffffff: scan phy phy at address 30
[    9.404941] mdio_bus  ethernet-ffffffff: scan phy phy at address 31
        for_each_available_child_of_node(np, child) {
                /* Skip PHYs with reg property set */
                if (of_find_property(child, "reg", NULL))
                        continue;

                for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
                        /* skip already registered PHYs */
                        if (mdiobus_is_registered_device(mdio, addr))
                                continue;

                        /* be noisy to encourage people to set reg property */
                        dev_info(&mdio->dev, "scan phy %pOFn at address %i
",
                                 child, addr);

                        if (of_mdiobus_child_is_phy(child)) {
                                /* -ENODEV is the return code that PHYLIB has
                                 * standardized on to indicate that bus
                                 * scanning should continue.
                                 */
                                rc = of_mdiobus_register_phy(mdio, child, addr);
                                if (!rc)
                                        break;
                                if (rc != -ENODEV)
                                        goto unregister;
                        }
                }
        }

phy-handle

  phy_node = of_parse_phandle(np, "phy-handle", 0);
    if (!phy_node && of_phy_is_fixed_link(np)) {
        ret = of_phy_register_fixed_link(np);
        if (ret < 0) {
            dev_err(&pdev->dev,
                "broken fixed-link specification
");
            goto failed_phy;
        }
        phy_node = of_node_get(np);
    }

 dma_map_single

 socket: Address family not supported by protocol

Could not attach PHY

Starting network: [ 54.898763] macb 20030000.ethernet eth0: Could not attach PHY (-19)
ip: SIOCSIFFLAGS: No such device

# ip link set eth0 up
[  726.996424] macb 20030000.ethernet eth0: Could not attach PHY (-19)
ip: SIOCSIFFLAGS: No such device
# 
# 
static int macb_phylink_connect(struct macb *bp)
{
        struct device_node *dn = bp->pdev->dev.of_node;
        struct net_device *dev = bp->dev;
        struct phy_device *phydev;
        int ret;

        if (dn)
                ret = phylink_of_phy_connect(bp->phylink, dn, 0);

        if (!dn || (ret && !macb_phy_handle_exists(dn))) {
                phydev = phy_find_first(bp->mii_bus);
                if (!phydev) {
                        netdev_err(dev, "no PHY found
");
                        return -ENXIO;
                }

                /* attach the mac to the phy */
                ret = phylink_connect_phy(bp->phylink, phydev);
        }

        if (ret) {
                netdev_err(dev, "Could not attach PHY (%d)
", ret);
                return ret;
        }

        phylink_start(bp->phylink);

        return 0;
}
static const struct of_device_id whitelist_phys[] = {
        { .compatible = "brcm,40nm-ephy" },
        { .compatible = "broadcom,bcm5241" },
        { .compatible = "marvell,88E1111", },
        { .compatible = "marvell,88e1116", },
        { .compatible = "marvell,88e1118", },
        { .compatible = "marvell,88e1145", },
        { .compatible = "marvell,88e1149r", },
        { .compatible = "marvell,88e1310", },
        { .compatible = "marvell,88E1510", },
        { .compatible = "marvell,88E1514", },
        { .compatible = "moxa,moxart-rtl8201cp", },
        {}
};
bool of_mdiobus_child_is_phy(struct device_node *child)
{
        u32 phy_id;

        if (of_get_phy_id(child, &phy_id) != -EINVAL)
                return true;

        if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"))
                return true;

        if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22"))
                return true;

        if (of_match_node(whitelist_phys, child)) {
                pr_warn(FW_WARN
                        "%pOF: Whitelisted compatible string. Please remove
",
                        child);
                return true;
        }

        if (!of_find_property(child, "compatible", NULL))
                return true;

        return false;
}

ethernet-phy-ieee802.3-c45

int fwnode_mdiobus_register_phy(struct mii_bus *bus,
                                struct fwnode_handle *child, u32 addr)
{
        struct mii_timestamper *mii_ts = NULL;
        struct phy_device *phy;
        bool is_c45 = false;
        u32 phy_id;
        int rc;

        mii_ts = fwnode_find_mii_timestamper(child);
        if (IS_ERR(mii_ts))
                return PTR_ERR(mii_ts);

        rc = fwnode_property_match_string(child, "compatible",
                                          "ethernet-phy-ieee802.3-c45");
        if (rc >= 0)
                is_c45 = true;

        if (is_c45 || fwnode_get_phy_id(child, &phy_id))
                phy = get_phy_device(bus, addr, is_c45);
        else
                phy = phy_device_create(bus, addr, phy_id, 0, NULL);

phy_id

.phy_id = MARVELL_PHY_ID_88E1111,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1111",
/* PHY_GBIT_FEATURES */
.probe = marvell_probe,
.config_init = m88e1111gbe_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
.get_tunable = m88e1111_get_tunable,
.set_tunable = m88e1111_set_tunable,
int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id)
{
        unsigned int upper, lower;
        const char *cp;
        int ret;

        ret = fwnode_property_read_string(fwnode, "compatible", &cp);
        if (ret)
                return ret;

        if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2)
                return -EINVAL;

        *phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0));
        return 0;
}

mdio_bus bus_probe_device

  18.700923] bus: 'mdio_bus': tracing bus_probe_device  20030000.ethernet-ffffffff:00
[   18.717777] traing__device_attach_driver  drv Generic Clause 45 PHY  match device 20030000.ethernet-ffffffff:00 : 0
[   18.732672] traing__device_attach_driver  drv Generic PHY  match device 20030000.ethernet-ffffffff:00 : 0
[   18.746344] traing__device_attach_driver  drv Marvell 88E1101  match device 20030000.ethernet-ffffffff:00 : 0
[   18.760473] traing__device_attach_driver  drv Marvell 88E1112  match device 20030000.ethernet-ffffffff:00 : 0
[   18.774620] traing__device_attach_driver  drv Marvell 88E1111  match device 20030000.ethernet-ffffffff:00 : 1
[   18.788744] bus: 'mdio_bus': __driver_probe_device: matched device 20030000.ethernet-ffffffff:00 with driver Marvell 88E1111
[   18.804915] Marvell 88E1111: call_driver_probe of 20030000.ethernet-ffffffff:00 
[   18.815722] tracing marvell_hwmon_probe 

static int m88e1111gbe_config_init(struct phy_device *phydev)
{
        int err;

        // tracing m88e1111_set_downshift
        err = m88e1111_set_downshift(phydev, 3);
        pr_err(" tracing m88e1111_set_downshift %d ",err );
        if (err < 0)
                return err;
        return m88e1111_config_init(phydev);
}
/**
 * __phy_modify_changed() - Convenience function for modifying a PHY register
 * @phydev: a pointer to a &struct phy_device
 * @regnum: register number
 * @mask: bit mask of bits to clear
 * @set: bit mask of bits to set
 *
 * Unlocked helper function which allows a PHY register to be modified as
 * new register value = (old register value & ~mask) | set
 *
 * Returns negative errno, 0 if there was no change, and 1 in case of change
 */
static inline int __phy_modify_changed(struct phy_device *phydev, u32 regnum,
                                       u16 mask, u16 set)
{
        return __mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr,
                                        regnum, mask, set);
}
/**
 * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
 * @bus: the mii_bus struct
 * @addr: the phy address
 * @regnum: register number to modify
 * @mask: bit mask of bits to clear
 * @set: bit mask of bits to set
 *
 * Read, modify, and if any change, write the register value back to the
 * device. Any error returns a negative number.
 *
 * NOTE: MUST NOT be called from interrupt context.
 */
int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
                 u16 mask, u16 set)
{
    int new, ret;

    ret = __mdiobus_read(bus, addr, regnum);
    if (ret < 0)
        return ret;

    new = (ret & ~mask) | set;
    if (new == ret)
        return 0;

    ret = __mdiobus_write(bus, addr, regnum, new);

    return ret < 0 ? ret : 1;
}
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
{
    int retval;

    lockdep_assert_held_once(&bus->mdio_lock);

    retval = bus->read(bus, addr, regnum);

    trace_mdio_access(bus, 1, addr, regnum, retval, retval);
    mdiobus_stats_acct(&bus->stats[addr], true, retval);

    return retval;
}
EXPORT_SYMBOL(__mdiobus_read);

/**
 * __mdiobus_write - Unlocked version of the mdiobus_write function
 * @bus: the mii_bus struct
 * @addr: the phy address
 * @regnum: register number to write
 * @val: value to write to @regnum
 *
 * Write a MDIO bus register. Caller must hold the mdio bus lock.
 *
 * NOTE: MUST NOT be called from interrupt context.
 */
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
{
    int err;

    lockdep_assert_held_once(&bus->mdio_lock);

    err = bus->write(bus, addr, regnum, val);

    trace_mdio_access(bus, 0, addr, regnum, val, err);

mdiobus_stats_acct(&bus->stats[addr], false, err); return err; }

struct mii_bus

mdiobus_register(bp->mii_bus);

static int macb_mii_init(struct macb *bp)
{
        int err = -ENXIO;

        /* Enable management port */
        macb_writel(bp, NCR, MACB_BIT(MPE));

        bp->mii_bus = mdiobus_alloc();
        if (!bp->mii_bus) {
                err = -ENOMEM;
                goto err_out;
        }

        bp->mii_bus->name = "MACB_mii_bus";
        bp->mii_bus->read = &macb_mdio_read;
        bp->mii_bus->write = &macb_mdio_write;
        snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
                 bp->pdev->name, bp->pdev->id);
        bp->mii_bus->priv = bp;
        bp->mii_bus->parent = &bp->pdev->dev;

        dev_set_drvdata(&bp->dev->dev, bp->mii_bus);

        err = macb_mdiobus_register(bp);
        pr_err("tracing macb_mdiobus_register %d.
", err);
        if (err)
                goto err_out_free_mdiobus;

        err = macb_mii_probe(bp->dev);
        pr_err("tracing macb_mdiobus_register %d.
", err);
        if (err)
                goto err_out_unregister_bus;

        return 0;

err_out_unregister_bus:
        mdiobus_unregister(bp->mii_bus);
err_out_free_mdiobus:
        mdiobus_free(bp->mii_bus);
err_out:
        return err;
}
__mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) 最终调用macb_mdio_write(
 
static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
                           u16 value)
{
        struct macb *bp = bus->priv;
        int status;

        status = pm_runtime_get_sync(&bp->pdev->dev);
        pr_err("tracing pm_runtime_get_sync %d", status);
        if (status < 0) { 
                pm_runtime_put_noidle(&bp->pdev->dev);
                goto mdio_pm_exit;
        }    

        status = macb_mdio_wait_for_idle(bp);
        pr_err("tracing macb_mdio_wait_for_idle %d", status);
        if (status < 0) 
                goto mdio_write_exit;

        if (regnum & MII_ADDR_C45) {
                macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF)
                            | MACB_BF(RW, MACB_MAN_C45_ADDR)
                            | MACB_BF(PHYA, mii_id)
                            | MACB_BF(REGA, (regnum >> 16) & 0x1F)
                            | MACB_BF(DATA, regnum & 0xFFFF)
                            | MACB_BF(CODE, MACB_MAN_C45_CODE)));

                status = macb_mdio_wait_for_idle(bp);
                if (status < 0) 
                        goto mdio_write_exit;

                macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF)
                            | MACB_BF(RW, MACB_MAN_C45_WRITE)
                            | MACB_BF(PHYA, mii_id)
                            | MACB_BF(REGA, (regnum >> 16) & 0x1F)
                            | MACB_BF(CODE, MACB_MAN_C45_CODE)
                            | MACB_BF(DATA, value)));
        } else {
                macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C22_SOF)
                                | MACB_BF(RW, MACB_MAN_C22_WRITE)
                                | MACB_BF(PHYA, mii_id)
                                | MACB_BF(REGA, regnum)
                                | MACB_BF(CODE, MACB_MAN_C22_CODE)
                                | MACB_BF(DATA, value)));
        }    

        status = macb_mdio_wait_for_idle(bp);if (status < 0) 
                goto mdio_write_exit;

mdio_write_exit:
        pm_runtime_mark_last_busy(&bp->pdev->dev);
        pm_runtime_put_autosuspend(&bp->pdev->dev);
mdio_pm_exit:
        return status;
}
Marvell 88E1111  phy_poll_reset failed









macb_probe->phy_connect_direct->phy_attach_direct->phy_init_hw()->
genphy_soft_reset()->phy_poll_reset

phy_poll_reset tries to do reset PHY by clearing the BMCR_RESET.

int phy_init_hw(struct phy_device *phydev)
{
       int ret = 0;

       /* Deassert the reset signal */
       phy_device_reset(phydev, 0);

       if (!phydev->drv || !phydev->drv->config_init)
               return 0;

       if (phydev->drv->soft_reset)
               ret = phydev->drv->soft_reset(phydev);
       else
               ret = genphy_soft_reset(phydev);

  ...
}

static int phy_poll_reset(struct phy_device *phydev)
{
       /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
       unsigned int retries = 12;
       int ret;

       dump_stack();
       do {
               msleep(50);
               ret = phy_read(phydev, MII_BMCR);
               if (ret < 0)
                       return ret;
       } while (ret & BMCR_RESET && --retries);
       if (ret & BMCR_RESET)
               return -ETIMEDOUT;

       /* Some chips (smsc911x) may still need up to another 1ms after the
        * BMCR_RESET bit is cleared before they are usable.
        */
       msleep(1);
       return 0;
}




Name

phy_poll_reset — Safely wait until a PHY reset has properly completed



/**
 * read_poll_timeout - Periodically poll an address until a condition is
 *                      met or a timeout occurs
 * @op: accessor function (takes @args as its arguments)
 * @val: Variable to read the value into
 * @cond: Break condition (usually involving @val)
 * @sleep_us: Maximum time to sleep between reads in us (0
 *            tight-loops).  Should be less than ~20ms since usleep_range
 *            is used (see Documentation/timers/timers-howto.rst).
 * @timeout_us: Timeout in us, 0 means never timeout
 * @sleep_before_read: if it is true, sleep @sleep_us before read.
 * @args: arguments for @op poll
 *
 * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
 * case, the last read value at @args is stored in @val. Must not
 * be called from atomic context if sleep_us or timeout_us are used.
 *
 * When available, you'll probably want to use one of the specialized
 * macros defined below rather than this macro directly.
 */
#define read_poll_timeout(op, val, cond, sleep_us, timeout_us, 
                                sleep_before_read, args...) 
({ 
        u64 __timeout_us = (timeout_us); 
        unsigned long __sleep_us = (sleep_us); 
        ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); 
        might_sleep_if((__sleep_us) != 0); 
        if (sleep_before_read && __sleep_us) 
                usleep_range((__sleep_us >> 2) + 1, __sleep_us); 
        for (;;) { 
                (val) = op(args); 
                if (cond) 
                        break; 
                if (__timeout_us && 
                    ktime_compare(ktime_get(), __timeout) > 0) { 
                        (val) = op(args); 
                        break; 
                } 
                if (__sleep_us) 
                        usleep_range((__sleep_us >> 2) + 1, __sleep_us); 
        } 
        (cond) ? 0 : -ETIMEDOUT; 
})
 
/**
 * phy_poll_reset - Safely wait until a PHY reset has properly completed
 * @phydev: The PHY device to poll
 *
 * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as
 *   published in 2008, a PHY reset may take up to 0.5 seconds.  The MII BMCR
 *   register must be polled until the BMCR_RESET bit clears.
 *
 *   Furthermore, any attempts to write to PHY registers may have no effect
 *   or even generate MDIO bus errors until this is complete.
 *
 *   Some PHYs (such as the Marvell 88E1111) don't entirely conform to the
 *   standard and do not fully reset after the BMCR_RESET bit is set, and may
 *   even *REQUIRE* a soft-reset to properly restart autonegotiation.  In an
 *   effort to support such broken PHYs, this function is separate from the
 *   standard phy_init_hw() which will zero all the other bits in the BMCR
 *   and reapply all driver-specific and board-specific fixups.
 */
static int phy_poll_reset(struct phy_device *phydev)
{
        /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
        int ret, val;

        ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
                                    50000, 600000, true);
        pr_err("tracing phy_read_poll_timeout %d ******** 
" , ret);
        if (ret)
                return ret;
        /* Some chips (smsc911x) may still need up to another 1ms after the
         * BMCR_RESET bit is cleared before they are usable.
         */
        msleep(1);
        return 0;
}
 
#define phy_read_poll_timeout(phydev, regnum, val, cond, sleep_us, 
                                timeout_us, sleep_before_read) 
({ 
        int __ret = read_poll_timeout(phy_read, val, (cond) || val < 0, 
                sleep_us, timeout_us, sleep_before_read, phydev, regnum); 
        if (val <  0) 
                __ret = val; 
        if (__ret) 
                phydev_err(phydev, "%s failed: %d
", __func__, __ret); 
        __ret; 
})




Linux Mii management/mdio子系统分析之三 mii_bus注册、注销及其驱动开发流程

marvell 88e1512 网络调试

platform总线设备驱动模型

手把手教Linux驱动10-platform总线详解

原文地址:https://www.cnblogs.com/dream397/p/15504559.html