}
/**
- * iort_pmsi_get_dev_id() - Get the device id for a device
+ * iort_msi_xlate() - Map a MSI input ID for a device
* @dev: The device for which the mapping is to be done.
- * @dev_id: The device ID found.
+ * @input_id: The device input ID.
+ * @fwnode: Pointer to store the fwnode.
*
- * Returns: 0 for successful find a dev id, -ENODEV on error
+ * Returns: mapped MSI ID on success, input ID otherwise
+ * On success, the fwnode pointer is initialized to the MSI
+ * controller fwnode handle.
*/
-int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
+u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode)
{
- int i, index;
+ struct acpi_iort_its_group *its;
struct acpi_iort_node *node;
+ u32 dev_id;
node = iort_find_dev_node(dev);
if (!node)
- return -ENODEV;
+ return input_id;
- index = iort_get_id_mapping_index(node);
- /* if there is a valid index, go get the dev_id directly */
- if (index >= 0) {
- if (iort_node_get_id(node, dev_id, index))
- return 0;
- } else {
- for (i = 0; i < node->mapping_count; i++) {
- if (iort_node_map_platform_id(node, dev_id,
- IORT_MSI_TYPE, i))
- return 0;
- }
- }
+ node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
+ if (!node)
+ return input_id;
- return -ENODEV;
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)node->node_data;
+
+ *fwnode = iort_find_domain_token(its->identifiers[0]);
+
+ return dev_id;
}
-static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
{
struct iort_its_msi_chip *its_msi_chip;
int ret = -ENODEV;
spin_lock(&iort_msi_chip_lock);
list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
- if (its_msi_chip->translation_id == its_id) {
+ if (its_msi_chip->fw_node == node) {
*base = its_msi_chip->base_addr;
ret = 0;
break;
return ret;
}
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+ struct fwnode_handle *fwnode = iort_find_domain_token(its_id);
+
+ if (!fwnode)
+ return -ENODEV;
+
+ return iort_its_translate_pa(fwnode, base);
+}
+
+/**
+ * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
+ * @dev: The device for which the mapping is to be done.
+ * @dev_id: The device ID found.
+ * @pa: optional pointer to store translate frame address.
+ *
+ * Returns: 0 for successful devid and pa retrieval, -ENODEV on error
+ */
+int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
+{
+ struct acpi_iort_node *node, *parent = NULL;
+ struct acpi_iort_its_group *its;
+ int i, index;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return -ENODEV;
+
+ index = iort_get_id_mapping_index(node);
+ /* if there is a valid index, go get the dev_id directly */
+ if (index >= 0) {
+ parent = iort_node_get_id(node, dev_id, index);
+ } else {
+ for (i = 0; i < node->mapping_count; i++) {
+ parent = iort_node_map_platform_id(node, dev_id,
+ IORT_MSI_TYPE, i);
+ if (parent)
+ break;
+ }
+ }
+
+ if (!parent)
+ return -ENODEV;
+
+ if (pa) {
+ int ret;
+
+ its = (struct acpi_iort_its_group *)node->node_data;
+ ret = iort_find_its_base(its->identifiers[0], pa);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* iort_dev_find_its_id() - Find the ITS identifier for a device
* @dev: The device.
MSI_FLAG_PCI_MSIX | \
MSI_FLAG_MULTI_PCI_MSI)
-static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
+static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
{
struct resource res;
int ret;
- ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
- if (ret < 0)
- return ret;
+ if (is_of_node(msi_node)) {
+ struct device_node *msi_np = to_of_node(msi_node);
- ret = of_address_to_resource(msi_node, ret, &res);
- if (ret)
- return ret;
+ ret = of_property_match_string(msi_np, "reg-names", "ns-translate");
+ if (ret < 0)
+ return ret;
+
+ ret = of_address_to_resource(msi_np, ret, &res);
+ if (ret)
+ return ret;
+ } else {
+ ret = iort_its_translate_pa(msi_node, &res.start);
+ }
*pa = res.start;
return 0;
if (!msi_node)
return -ENODEV;
- ret = its_translate_frame_address(to_of_node(msi_node), &pa);
+ ret = its_translate_frame_address(msi_node, &pa);
if (ret)
return -ENODEV;
ret = -EINVAL;
if (!ret && pa)
- ret = its_translate_frame_address(it.node, pa);
+ ret = its_translate_frame_address(of_fwnode_handle(it.node), pa);
if (!ret)
*dev_id = args;
return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
}
-int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
-{
- return -1;
-}
-
static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *info)
{
if (dev->of_node)
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
else
- ret = iort_pmsi_get_dev_id(dev, &dev_id);
+ ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
if (ret)
return ret;
u32 dev_id;
int ret;
- if (!dev->of_node)
- return -ENODEV;
-
- ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+ if (dev->of_node)
+ ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+ else
+ ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa);
if (ret)
return ret;
{
struct gicv5_irs_chip_data *irs_data;
- list_for_each_entry(irs_data, &irs_nodes, entry)
- gicv5_its_of_probe(to_of_node(irs_data->fwnode));
+ if (acpi_disabled)
+ list_for_each_entry(irs_data, &irs_nodes, entry)
+ gicv5_its_of_probe(to_of_node(irs_data->fwnode));
+ else
+ gicv5_its_acpi_probe();
}
int __init gicv5_irs_of_probe(struct device_node *parent)
#define pr_fmt(fmt) "GICv5 ITS: " fmt
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
#include <linux/bitmap.h>
#include <linux/iommu.h>
#include <linux/init.h>
}
static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle,
- struct irq_domain *parent_domain)
+ struct irq_domain *parent_domain, bool noncoherent)
{
struct device_node *np = to_of_node(handle);
struct gicv5_its_chip_data *its_node;
}
ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node),
- gicv5_global_data.lpi_domain);
+ gicv5_global_data.lpi_domain,
+ of_property_read_bool(node, "dma-noncoherent"));
if (ret)
goto out_unmap;
pr_err("Failed to init ITS %s\n", np->full_name);
}
}
+
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K)
+
+static struct acpi_madt_gicv5_translator *current_its_entry __initdata;
+static struct fwnode_handle *current_its_fwnode __initdata;
+
+static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_gicv5_translate_frame *its_frame;
+ struct fwnode_handle *msi_dom_handle;
+ struct resource res = {};
+ int err;
+
+ its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
+ if (its_frame->linked_translator_id != current_its_entry->translator_id)
+ return 0;
+
+ res.start = its_frame->base_address;
+ res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
+ res.flags = IORESOURCE_MEM;
+
+ msi_dom_handle = irq_domain_alloc_parented_fwnode(&res.start, current_its_fwnode);
+ if (!msi_dom_handle) {
+ pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n",
+ &res.start);
+ return -ENOMEM;
+ }
+
+ err = iort_register_domain_token(its_frame->translate_frame_id, res.start,
+ msi_dom_handle);
+ if (err) {
+ pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n",
+ &res.start, its_frame->translate_frame_id);
+ irq_domain_free_fwnode(msi_dom_handle);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_gicv5_translate_frame *its_frame;
+ struct fwnode_handle *msi_dom_handle;
+
+ its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
+ if (its_frame->linked_translator_id != current_its_entry->translator_id)
+ return 0;
+
+ msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id);
+ if (!msi_dom_handle)
+ return 0;
+
+ iort_deregister_domain_token(its_frame->translate_frame_id);
+ irq_domain_free_fwnode(msi_dom_handle);
+
+ return 0;
+}
+
+static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_gicv5_translator *its_entry;
+ struct fwnode_handle *dom_handle;
+ struct resource res = {};
+ void __iomem *its_base;
+ int err;
+
+ its_entry = (struct acpi_madt_gicv5_translator *)header;
+ res.start = its_entry->base_address;
+ res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
+ res.flags = IORESOURCE_MEM;
+
+ if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS"))
+ return -EBUSY;
+
+ dom_handle = irq_domain_alloc_fwnode(&res.start);
+ if (!dom_handle) {
+ pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n",
+ &res.start);
+ err = -ENOMEM;
+ goto out_rel_res;
+ }
+
+ current_its_entry = its_entry;
+ current_its_fwnode = dom_handle;
+
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
+ gic_acpi_parse_madt_its_translate, 0);
+
+ its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE);
+ if (!its_base) {
+ err = -ENOMEM;
+ goto out_unregister;
+ }
+
+ err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain,
+ its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT);
+ if (err)
+ goto out_unmap;
+
+ return 0;
+
+out_unmap:
+ iounmap(its_base);
+out_unregister:
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
+ gic_acpi_free_madt_its_translate, 0);
+ irq_domain_free_fwnode(dom_handle);
+out_rel_res:
+ release_mem_region(res.start, resource_size(&res));
+ return err;
+}
+
+void __init gicv5_its_acpi_probe(void)
+{
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0);
+}
+#else
+void __init gicv5_its_acpi_probe(void) { }
+#endif
rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
if (msi_ctlr_node)
*node = of_fwnode_handle(msi_ctlr_node);
+ } else {
+ rid = iort_msi_xlate(&pdev->dev, rid, node);
}
return rid;
struct fwnode_handle *fw_node);
void iort_deregister_domain_token(int trans_id);
struct fwnode_handle *iort_find_domain_token(int trans_id);
-int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
#ifdef CONFIG_ACPI_IORT
u32 iort_msi_map_id(struct device *dev, u32 id);
+u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node);
+int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base);
struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
enum irq_domain_bus_token bus_token);
+int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa);
void acpi_configure_pmsi_domain(struct device *dev);
void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
struct list_head *head);
#else
static inline u32 iort_msi_map_id(struct device *dev, u32 id)
{ return id; }
+static inline u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node)
+{ return id; }
+static inline int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
+{ return -ENODEV; }
static inline struct irq_domain *iort_get_device_domain(
struct device *dev, u32 id, enum irq_domain_bus_token bus_token)
{ return NULL; }
+static inline int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
+{ return -ENODEV; }
static inline void acpi_configure_pmsi_domain(struct device *dev) { }
static inline
void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head) { }
void gicv5_free_lpi(u32 lpi);
void __init gicv5_its_of_probe(struct device_node *parent);
+void __init gicv5_its_acpi_probe(void);
#endif