Commit ab6484ee authored by Marc Zyngier's avatar Marc Zyngier

platform-msi: Allow MSIs to be allocated in chunks

MSIs for a given device are normally all allocated in one go.
Make sure the internal code can allocate them one at a time
if required.
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 9f9499ae
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#define DEV_ID_SHIFT 24 #define DEV_ID_SHIFT 24
#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
/* /*
* Internal data structure containing a (made up, but unique) devid * Internal data structure containing a (made up, but unique) devid
...@@ -110,13 +111,16 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info) ...@@ -110,13 +111,16 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info)
chip->irq_write_msi_msg = platform_msi_write_msg; chip->irq_write_msi_msg = platform_msi_write_msg;
} }
static void platform_msi_free_descs(struct device *dev) static void platform_msi_free_descs(struct device *dev, int base, int nvec)
{ {
struct msi_desc *desc, *tmp; struct msi_desc *desc, *tmp;
list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
list_del(&desc->list); if (desc->platform.msi_index >= base &&
free_msi_entry(desc); desc->platform.msi_index < (base + nvec)) {
list_del(&desc->list);
free_msi_entry(desc);
}
} }
} }
...@@ -124,17 +128,22 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec, ...@@ -124,17 +128,22 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
struct platform_msi_priv_data *data) struct platform_msi_priv_data *data)
{ {
int i; struct msi_desc *desc;
int i, base = 0;
for (i = 0; i < nvec; i++) { if (!list_empty(dev_to_msi_list(dev))) {
struct msi_desc *desc; desc = list_last_entry(dev_to_msi_list(dev),
struct msi_desc, list);
base = desc->platform.msi_index + 1;
}
for (i = 0; i < nvec; i++) {
desc = alloc_msi_entry(dev); desc = alloc_msi_entry(dev);
if (!desc) if (!desc)
break; break;
desc->platform.msi_priv_data = data; desc->platform.msi_priv_data = data;
desc->platform.msi_index = i; desc->platform.msi_index = base + i;
desc->nvec_used = 1; desc->nvec_used = 1;
list_add_tail(&desc->list, dev_to_msi_list(dev)); list_add_tail(&desc->list, dev_to_msi_list(dev));
...@@ -142,7 +151,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec, ...@@ -142,7 +151,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
if (i != nvec) { if (i != nvec) {
/* Clean up the mess */ /* Clean up the mess */
platform_msi_free_descs(dev); platform_msi_free_descs(dev, base, nvec);
return -ENOMEM; return -ENOMEM;
} }
...@@ -201,8 +210,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, ...@@ -201,8 +210,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
* accordingly (which would impact the max number of MSI * accordingly (which would impact the max number of MSI
* capable devices). * capable devices).
*/ */
if (!dev->msi_domain || !write_msi_msg || !nvec || if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
nvec > (1 << (32 - DEV_ID_SHIFT)))
return -EINVAL; return -EINVAL;
if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
...@@ -238,7 +246,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, ...@@ -238,7 +246,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
return 0; return 0;
out_free_desc: out_free_desc:
platform_msi_free_descs(dev); platform_msi_free_descs(dev, 0, nvec);
out_free_id: out_free_id:
ida_simple_remove(&platform_msi_devid_ida, priv_data->devid); ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
out_free_data: out_free_data:
...@@ -266,5 +274,5 @@ void platform_msi_domain_free_irqs(struct device *dev) ...@@ -266,5 +274,5 @@ void platform_msi_domain_free_irqs(struct device *dev)
} }
msi_domain_free_irqs(dev->msi_domain, dev); msi_domain_free_irqs(dev->msi_domain, dev);
platform_msi_free_descs(dev); platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment