在acpi bus scan的时候会为name为Hisilicon MBIGEN-V2建立platform_device.因此在mbigen的初始化中只要调用platform_driver_register 即可。code如下:
static __init int mbigen_init(void)
{
return platform_driver_register(&mbigen_platform_driver);
}
arch_initcall(mbigen_init);
static struct platform_driver mbigen_platform_driver = {
.driver = {
.name = "Hisilicon MBIGEN-V2",
.of_match_table = mbigen_of_match,
.acpi_match_table = ACPI_PTR(mbigen_acpi_match),
},
.probe = mbigen_device_probe,
};
mbigen_platform_driver 中定义了devicetree和acpi的match table,这两个table是同一个,这里可知这个driver同时support device tree和 acpi。同时也可以知道一个driver要同事support device和acpi,就要分别定义of_match_table 和 acpi_match_table.
如果match了,就调用mbigen_device_probe
static int mbigen_device_probe(struct platform_device *pdev)
{
struct mbigen_device *mgn_chip;
struct resource *res;
int err;
//申请mbigen_device 结构体
mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
if (!mgn_chip)
return -ENOMEM;
mgn_chip->pdev = pdev;
//从dev的资源中找到类型是IORESOURCE_MEM。找到后调用devm_ioremap,就将物理地址转成虚拟地址了
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mgn_chip->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (IS_ERR(mgn_chip->base))
return PTR_ERR(mgn_chip->base);
//判断当前是采用device tree还是acpi
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
err = mbigen_of_create_domain(pdev, mgn_chip);
else if (ACPI_COMPANION(&pdev->dev))
err = mbigen_acpi_create_domain(pdev, mgn_chip);
else
err = -EINVAL;
if (err) {
dev_err(&pdev->dev, "Failed to create mbi-gen@%p irqdomain", mgn_chip->base);
return err;
}
platform_set_drvdata(pdev, mgn_chip);
return 0;
}
我们这里以acpi为主,就调用mbigen_acpi_create_domain
static int mbigen_acpi_create_domain(struct platform_device *pdev,
struct mbigen_device *mgn_chip)
{
struct irq_domain *domain;
u32 num_msis = 0;
acpi_status status;
status = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), METHOD_NAME__PRS,
mbigen_acpi_process_resource, &num_msis);
if (ACPI_FAILURE(status) || num_msis == 0)
return -EINVAL;
domain = platform_msi_create_device_domain(&pdev->dev, num_msis,
mbigen_write_msg,
&mbigen_domain_ops,
mgn_chip);
if (!domain)
return -ENOMEM;
return 0;
}
在mbigen_acpi_create_domain 中调用acpi_walk_resources 遍历#define METHOD_NAME__PRS "_PRS"。如果找到就调用
static acpi_status mbigen_acpi_process_resource(struct acpi_resource *ares,
void *context)
{
struct acpi_resource_extended_irq *ext_irq;
u32 *num_irqs = context;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
ext_irq = &ares->data.extended_irq;
*num_irqs += ext_irq->interrupt_count;
break;
default:
break;
}
return AE_OK;
}
这个函数的第二个形参context,就是acpi_walk_resources 中的num_msis。因此这里主要判断统计ACPI_RESOURCE_TYPE_EXTENDED_IRQ 类型的中断,将结果保存在num_msis中。
然后调用platform_msi_create_device_domain 来创建domain,之一这里的ops是
static struct irq_domain_ops mbigen_domain_ops = {
.translate = mbigen_domain_translate,
.alloc = mbigen_irq_domain_alloc,
.free = irq_domain_free_irqs_common,
};
struct irq_domain *
platform_msi_create_device_domain(struct device *dev,
unsigned int nvec,
irq_write_msi_msg_t write_msi_msg,
const struct irq_domain_ops *ops,
void *host_data)
{
struct platform_msi_priv_data *data;
struct irq_domain *domain;
int err;
data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
if (IS_ERR(data))
return NULL;
//保存host data到platform_msi_priv_data 中,这里的host data是指mgn_chip
data->host_data = host_data;
domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
dev->fwnode, ops, data);
if (!domain)
goto free_priv;
err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
if (err)
goto free_domain;
return domain;
}
在platform_msi_create_device_domain 中首先调用platform_msi_alloc_priv_data,来申请platform_msi_priv_data *data
static struct platform_msi_priv_data *
platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
irq_write_msi_msg_t write_msi_msg)
{
struct platform_msi_priv_data *datap;
datap = kzalloc(sizeof(*datap), GFP_KERNEL);
if (!datap)
return ERR_PTR(-ENOMEM);
datap->devid = ida_simple_get(&platform_msi_devid_ida,
0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
if (datap->devid < 0) {
int err = datap->devid;
kfree(datap);
return ERR_PTR(err);
}
datap->write_msg = write_msi_msg;
datap->dev = dev;
return datap;
}
在platform_msi_alloc_priv_data 中是要是申请platform_msi_priv_data 结构体,然后给write_msg/devid/dev 赋值.
回到platform_msi_create_device_domain然后调用
struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
unsigned int flags,
unsigned int size,
struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
if (size)
domain = irq_domain_create_linear(fwnode, size, ops, host_data);
else
domain = irq_domain_create_tree(fwnode, ops, host_data);
if (domain) {
domain->parent = parent;
domain->flags |= flags;
}
return domain;
}
这里的size就是前面的num_msis,肯定不为0.因此走irq_domain_create_linear->__irq_domain_add
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
struct device_node *of_node = to_of_node(fwnode);
struct irq_domain *domain;
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
GFP_KERNEL, of_node_to_nid(of_node));
if (WARN_ON(!domain))
return NULL;
of_node_get(of_node);
/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->ops = ops;
domain->host_data = host_data;
domain->fwnode = fwnode;
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
irq_domain_check_hierarchy(domain);
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);
mutex_unlock(&irq_domain_mutex);
pr_debug("Added domain %s\n", domain->name);
return domain;
}
这里主要是给domain 赋值,然后降到irq_domain_list中,可见所有的domain 都会在irq_domain_list。最后会打印Added domain log。可以从开机log中就可以看到有多少domain 添加到irq_domain_list
回到platform_msi_create_device_domain中继续调用msi_domain_prepare_irqs
int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *arg)
{
struct msi_domain_info *info = domain->host_data;
struct msi_domain_ops *ops = info->ops;
int ret;
ret = ops->msi_check(domain, info, dev);
if (ret == 0)
ret = ops->msi_prepare(domain, dev, nvec, arg);
return ret;
}
这个主要调用msi_domain_ops的msi_check和msi_prepare 方法,这里的ops注意是host_data 中的,而host_data 是在platform_msi_create_device_domain中保存到data->host_data = host_data;
而host_data 中的ops是在its_pmsi_init->platform_msi_create_irq_domain->msi_create_irq_domain->msi_domain_update_dom_ops 中赋值的
static void msi_domain_update_dom_ops(struct msi_domain_info *info)
{
struct msi_domain_ops *ops = info->ops;
if (ops == NULL) {
info->ops = &msi_domain_ops_default;
return;
}
if (ops->get_hwirq == NULL)
ops->get_hwirq = msi_domain_ops_default.get_hwirq;
if (ops->msi_init == NULL)
ops->msi_init = msi_domain_ops_default.msi_init;
if (ops->msi_check == NULL)
ops->msi_check = msi_domain_ops_default.msi_check;
if (ops->msi_prepare == NULL)
ops->msi_prepare = msi_domain_ops_default.msi_prepare;
if (ops->set_desc == NULL)
ops->set_desc = msi_domain_ops_default.set_desc;
}
因此在msi_domain_prepare_irqs 中调用的msi_check和msi_prepare 就是调用msi_domain_ops_default 中的方法
static int msi_domain_ops_check(struct irq_domain *domain,
struct msi_domain_info *info,
struct device *dev)
{
return 0;
}
直接返回0,因此也不用调用msi_prepare了.
转载请注明原文地址: https://ju.6miu.com/read-38783.html