/** * struct qcom_ipcc_chan_info - Per-mailbox-channel info * @client_id: The client-id to which the interrupt has to be triggered * @signal_id: The signal-id to which the interrupt has to be triggered
*/ struct qcom_ipcc_chan_info {
u16 client_id;
u16 signal_id;
};
/** * struct qcom_ipcc - Holder for the mailbox driver * @dev: Device associated with this instance * @base: Base address of the IPCC frame associated to APSS * @irq_domain: The irq_domain associated with this instance * @chans: The mailbox channels array * @mchan: The per-mailbox channel info array * @mbox: The mailbox controller * @num_chans: Number of @chans elements * @irq: Summary irq
*/ struct qcom_ipcc { struct device *dev; void __iomem *base; struct irq_domain *irq_domain; struct mbox_chan *chans; struct qcom_ipcc_chan_info *mchan; struct mbox_controller mbox; int num_chans; int irq;
};
/* * Find out the number of clients interested in this mailbox * and create channels accordingly.
*/
ipcc->num_chans = 0;
for_each_node_with_property(client_dn, "mboxes") { if (!of_device_is_available(client_dn)) continue;
i = of_count_phandle_with_args(client_dn, "mboxes", "#mbox-cells"); for (j = 0; j < i; j++) {
ret = of_parse_phandle_with_args(client_dn, "mboxes", "#mbox-cells", j, &curr_ph);
of_node_put(curr_ph.np); if (!ret && curr_ph.np == controller_dn)
ipcc->num_chans++;
}
}
/* If no clients are found, skip registering as a mbox controller */ if (!ipcc->num_chans) return 0;
ipcc = devm_kzalloc(&pdev->dev, sizeof(*ipcc), GFP_KERNEL); if (!ipcc) return -ENOMEM;
ipcc->dev = &pdev->dev;
ipcc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ipcc->base)) return PTR_ERR(ipcc->base);
/* * It is possible that boot firmware is using the same IPCC instance * as of the HLOS and it has kept CLEAR_ON_RECV_RD set which basically * means Interrupt pending registers are cleared when RECV_ID is read. * The register automatically updates to the next pending interrupt/client * status based on priority.
*/
config_value = readl(ipcc->base + IPCC_REG_CONFIG); if (config_value & IPCC_CLEAR_ON_RECV_RD) {
config_value &= ~(IPCC_CLEAR_ON_RECV_RD);
writel(config_value, ipcc->base + IPCC_REG_CONFIG);
}
ipcc->irq = platform_get_irq(pdev, 0); if (ipcc->irq < 0) return ipcc->irq;
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ipcc_%d", id++); if (!name) return -ENOMEM;
ipcc->irq_domain = irq_domain_create_tree(dev_fwnode(&pdev->dev), &qcom_ipcc_irq_ops, ipcc); if (!ipcc->irq_domain) return -ENOMEM;
ret = qcom_ipcc_setup_mbox(ipcc, pdev->dev.of_node); if (ret) goto err_mbox;
ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn,
IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND |
IRQF_NO_THREAD, name, ipcc); if (ret < 0) {
dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret); goto err_req_irq;
}
platform_set_drvdata(pdev, ipcc);
return 0;
err_req_irq: if (ipcc->num_chans)
mbox_controller_unregister(&ipcc->mbox);
err_mbox:
irq_domain_remove(ipcc->irq_domain);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.