summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/component.c281
-rw-r--r--drivers/base/platform-msi.c256
-rw-r--r--drivers/base/platform.c20
-rw-r--r--drivers/base/regmap/regcache-flat.c2
-rw-r--r--drivers/base/regmap/regcache-lzo.c6
-rw-r--r--drivers/base/regmap/regcache-rbtree.c18
-rw-r--r--drivers/base/regmap/regcache.c41
-rw-r--r--drivers/base/regmap/regmap-debugfs.c69
-rw-r--r--drivers/base/regmap/regmap-irq.c113
-rw-r--r--drivers/base/regmap/regmap-mmio.c66
-rw-r--r--drivers/base/regmap/regmap.c118
11 files changed, 691 insertions, 299 deletions
diff --git a/drivers/base/component.c b/drivers/base/component.c
index f748430bb654..89f5cf68d80a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -18,18 +18,24 @@
#include <linux/mutex.h>
#include <linux/slab.h>
+struct component;
+
+struct component_match_array {
+ void *data;
+ int (*compare)(struct device *, void *);
+ void (*release)(struct device *, void *);
+ struct component *component;
+ bool duplicate;
+};
+
struct component_match {
size_t alloc;
size_t num;
- struct {
- void *data;
- int (*fn)(struct device *, void *);
- } compare[0];
+ struct component_match_array *compare;
};
struct master {
struct list_head node;
- struct list_head components;
bool bound;
const struct component_master_ops *ops;
@@ -39,7 +45,6 @@ struct master {
struct component {
struct list_head node;
- struct list_head master_node;
struct master *master;
bool bound;
@@ -63,48 +68,21 @@ static struct master *__master_find(struct device *dev,
return NULL;
}
-/* Attach an unattached component to a master. */
-static void component_attach_master(struct master *master, struct component *c)
-{
- c->master = master;
-
- list_add_tail(&c->master_node, &master->components);
-}
-
-/* Detach a component from a master. */
-static void component_detach_master(struct master *master, struct component *c)
-{
- list_del(&c->master_node);
-
- c->master = NULL;
-}
-
-/*
- * Add a component to a master, finding the component via the compare
- * function and compare data. This is safe to call for duplicate matches
- * and will not result in the same component being added multiple times.
- */
-int component_master_add_child(struct master *master,
+static struct component *find_component(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component *c;
- int ret = -ENXIO;
list_for_each_entry(c, &component_list, node) {
if (c->master && c->master != master)
continue;
- if (compare(c->dev, compare_data)) {
- if (!c->master)
- component_attach_master(master, c);
- ret = 0;
- break;
- }
+ if (compare(c->dev, compare_data))
+ return c;
}
- return ret;
+ return NULL;
}
-EXPORT_SYMBOL_GPL(component_master_add_child);
static int find_components(struct master *master)
{
@@ -112,39 +90,44 @@ static int find_components(struct master *master)
size_t i;
int ret = 0;
- if (!match) {
- /*
- * Search the list of components, looking for components that
- * belong to this master, and attach them to the master.
- */
- return master->ops->add_components(master->dev, master);
- }
-
/*
* Scan the array of match functions and attach
* any components which are found to this master.
*/
for (i = 0; i < match->num; i++) {
- ret = component_master_add_child(master,
- match->compare[i].fn,
- match->compare[i].data);
- if (ret)
+ struct component_match_array *mc = &match->compare[i];
+ struct component *c;
+
+ dev_dbg(master->dev, "Looking for component %zu\n", i);
+
+ if (match->compare[i].component)
+ continue;
+
+ c = find_component(master, mc->compare, mc->data);
+ if (!c) {
+ ret = -ENXIO;
break;
+ }
+
+ dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);
+
+ /* Attach this component to the master */
+ match->compare[i].duplicate = !!c->master;
+ match->compare[i].component = c;
+ c->master = master;
}
return ret;
}
-/* Detach all attached components from this master */
-static void master_remove_components(struct master *master)
+/* Detach component from associated master */
+static void remove_component(struct master *master, struct component *c)
{
- while (!list_empty(&master->components)) {
- struct component *c = list_first_entry(&master->components,
- struct component, master_node);
-
- WARN_ON(c->master != master);
+ size_t i;
- component_detach_master(master, c);
- }
+ /* Detach the component from this master. */
+ for (i = 0; i < master->match->num; i++)
+ if (master->match->compare[i].component == c)
+ master->match->compare[i].component = NULL;
}
/*
@@ -159,44 +142,32 @@ static int try_to_bring_up_master(struct master *master,
{
int ret;
- if (master->bound)
- return 0;
+ dev_dbg(master->dev, "trying to bring up master\n");
- /*
- * Search the list of components, looking for components that
- * belong to this master, and attach them to the master.
- */
if (find_components(master)) {
- /* Failed to find all components */
- ret = 0;
- goto out;
+ dev_dbg(master->dev, "master has incomplete components\n");
+ return 0;
}
if (component && component->master != master) {
- ret = 0;
- goto out;
+ dev_dbg(master->dev, "master is not for this component (%s)\n",
+ dev_name(component->dev));
+ return 0;
}
- if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
/* Found all components */
ret = master->ops->bind(master->dev);
if (ret < 0) {
devres_release_group(master->dev, NULL);
dev_info(master->dev, "master bind failed: %d\n", ret);
- goto out;
+ return ret;
}
master->bound = true;
return 1;
-
-out:
- master_remove_components(master);
-
- return ret;
}
static int try_to_bring_up_masters(struct component *component)
@@ -205,9 +176,11 @@ static int try_to_bring_up_masters(struct component *component)
int ret = 0;
list_for_each_entry(m, &masters, node) {
- ret = try_to_bring_up_master(m, component);
- if (ret != 0)
- break;
+ if (!m->bound) {
+ ret = try_to_bring_up_master(m, component);
+ if (ret != 0)
+ break;
+ }
}
return ret;
@@ -220,45 +193,57 @@ static void take_down_master(struct master *master)
devres_release_group(master->dev, NULL);
master->bound = false;
}
+}
- master_remove_components(master);
+static void component_match_release(struct device *master,
+ struct component_match *match)
+{
+ unsigned int i;
+
+ for (i = 0; i < match->num; i++) {
+ struct component_match_array *mc = &match->compare[i];
+
+ if (mc->release)
+ mc->release(master, mc->data);
+ }
}
-static size_t component_match_size(size_t num)
+static void devm_component_match_release(struct device *dev, void *res)
{
- return offsetof(struct component_match, compare[num]);
+ component_match_release(dev, res);
}
-static struct component_match *component_match_realloc(struct device *dev,
+static int component_match_realloc(struct device *dev,
struct component_match *match, size_t num)
{
- struct component_match *new;
+ struct component_match_array *new;
- if (match && match->alloc == num)
- return match;
+ if (match->alloc == num)
+ return 0;
- new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
+ new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL);
if (!new)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
- if (match) {
- memcpy(new, match, component_match_size(min(match->num, num)));
- devm_kfree(dev, match);
- } else {
- new->num = 0;
+ if (match->compare) {
+ memcpy(new, match->compare, sizeof(*new) *
+ min(match->num, num));
+ devm_kfree(dev, match->compare);
}
+ match->compare = new;
+ match->alloc = num;
- new->alloc = num;
-
- return new;
+ return 0;
}
/*
- * Add a component to be matched.
+ * Add a component to be matched, with a release function.
*
* The match array is first created or extended if necessary.
*/
-void component_match_add(struct device *dev, struct component_match **matchptr,
+void component_match_add_release(struct device *master,
+ struct component_match **matchptr,
+ void (*release)(struct device *, void *),
int (*compare)(struct device *, void *), void *compare_data)
{
struct component_match *match = *matchptr;
@@ -266,22 +251,37 @@ void component_match_add(struct device *dev, struct component_match **matchptr,
if (IS_ERR(match))
return;
- if (!match || match->num == match->alloc) {
- size_t new_size = match ? match->alloc + 16 : 15;
+ if (!match) {
+ match = devres_alloc(devm_component_match_release,
+ sizeof(*match), GFP_KERNEL);
+ if (!match) {
+ *matchptr = ERR_PTR(-ENOMEM);
+ return;
+ }
- match = component_match_realloc(dev, match, new_size);
+ devres_add(master, match);
*matchptr = match;
+ }
+
+ if (match->num == match->alloc) {
+ size_t new_size = match ? match->alloc + 16 : 15;
+ int ret;
- if (IS_ERR(match))
+ ret = component_match_realloc(master, match, new_size);
+ if (ret) {
+ *matchptr = ERR_PTR(ret);
return;
+ }
}
- match->compare[match->num].fn = compare;
+ match->compare[match->num].compare = compare;
+ match->compare[match->num].release = release;
match->compare[match->num].data = compare_data;
+ match->compare[match->num].component = NULL;
match->num++;
}
-EXPORT_SYMBOL(component_match_add);
+EXPORT_SYMBOL(component_match_add_release);
int component_master_add_with_match(struct device *dev,
const struct component_master_ops *ops,
@@ -290,15 +290,10 @@ int component_master_add_with_match(struct device *dev,
struct master *master;
int ret;
- if (ops->add_components && match)
- return -EINVAL;
-
- if (match) {
- /* Reallocate the match array for its true size */
- match = component_match_realloc(dev, match, match->num);
- if (IS_ERR(match))
- return PTR_ERR(match);
- }
+ /* Reallocate the match array for its true size */
+ ret = component_match_realloc(dev, match, match->num);
+ if (ret)
+ return ret;
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
@@ -307,7 +302,6 @@ int component_master_add_with_match(struct device *dev,
master->dev = dev;
master->ops = ops;
master->match = match;
- INIT_LIST_HEAD(&master->components);
/* Add to the list of available masters. */
mutex_lock(&component_mutex);
@@ -326,24 +320,28 @@ int component_master_add_with_match(struct device *dev,
}
EXPORT_SYMBOL_GPL(component_master_add_with_match);
-int component_master_add(struct device *dev,
- const struct component_master_ops *ops)
-{
- return component_master_add_with_match(dev, ops, NULL);
-}
-EXPORT_SYMBOL_GPL(component_master_add);
-
void component_master_del(struct device *dev,
const struct component_master_ops *ops)
{
struct master *master;
+ int i;
mutex_lock(&component_mutex);
master = __master_find(dev, ops);
if (master) {
+ struct component_match *match = master->match;
+
take_down_master(master);
list_del(&master->node);
+
+ if (match) {
+ for (i = 0; i < match->num; i++) {
+ struct component *c = match->compare[i].component;
+ if (c)
+ c->master = NULL;
+ }
+ }
kfree(master);
}
mutex_unlock(&component_mutex);
@@ -366,6 +364,7 @@ void component_unbind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
+ size_t i;
WARN_ON(!mutex_is_locked(&component_mutex));
@@ -373,8 +372,12 @@ void component_unbind_all(struct device *master_dev, void *data)
if (!master)
return;
- list_for_each_entry_reverse(c, &master->components, master_node)
- component_unbind(c, master, data);
+ /* Unbind components in reverse order */
+ for (i = master->match->num; i--; )
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ component_unbind(c, master, data);
+ }
}
EXPORT_SYMBOL_GPL(component_unbind_all);
@@ -434,6 +437,7 @@ int component_bind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
+ size_t i;
int ret = 0;
WARN_ON(!mutex_is_locked(&component_mutex));
@@ -442,16 +446,21 @@ int component_bind_all(struct device *master_dev, void *data)
if (!master)
return -EINVAL;
- list_for_each_entry(c, &master->components, master_node) {
- ret = component_bind(c, master, data);
- if (ret)
- break;
- }
+ /* Bind components in match order */
+ for (i = 0; i < master->match->num; i++)
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ ret = component_bind(c, master, data);
+ if (ret)
+ break;
+ }
if (ret != 0) {
- list_for_each_entry_continue_reverse(c, &master->components,
- master_node)
- component_unbind(c, master, data);
+ for (; i--; )
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ component_unbind(c, master, data);
+ }
}
return ret;
@@ -499,8 +508,10 @@ void component_del(struct device *dev, const struct component_ops *ops)
break;
}
- if (component && component->master)
+ if (component && component->master) {
take_down_master(component->master);
+ remove_component(component->master, component);
+ }
mutex_unlock(&component_mutex);
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 5df4575b5ba7..47c43386786b 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -24,13 +24,17 @@
#include <linux/msi.h>
#include <linux/slab.h>
-#define DEV_ID_SHIFT 24
+#define DEV_ID_SHIFT 21
+#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
/*
* Internal data structure containing a (made up, but unique) devid
* and the callback to write the MSI message.
*/
struct platform_msi_priv_data {
+ struct device *dev;
+ void *host_data;
+ msi_alloc_info_t arg;
irq_write_msi_msg_t write_msg;
int devid;
};
@@ -110,39 +114,49 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info)
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;
list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
- list_del(&desc->list);
- free_msi_entry(desc);
+ if (desc->platform.msi_index >= base &&
+ desc->platform.msi_index < (base + nvec)) {
+ list_del(&desc->list);
+ free_msi_entry(desc);
+ }
}
}
-static int platform_msi_alloc_descs(struct device *dev, int nvec,
- struct platform_msi_priv_data *data)
+static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
+ int nvec,
+ struct platform_msi_priv_data *data)
{
- int i;
+ struct msi_desc *desc;
+ int i, base = 0;
- for (i = 0; i < nvec; i++) {
- struct msi_desc *desc;
+ if (!list_empty(dev_to_msi_list(dev))) {
+ 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);
if (!desc)
break;
desc->platform.msi_priv_data = data;
- desc->platform.msi_index = i;
+ desc->platform.msi_index = base + i;
desc->nvec_used = 1;
+ desc->irq = virq ? virq + i : 0;
list_add_tail(&desc->list, dev_to_msi_list(dev));
}
if (i != nvec) {
/* Clean up the mess */
- platform_msi_free_descs(dev);
+ platform_msi_free_descs(dev, base, nvec);
return -ENOMEM;
}
@@ -150,6 +164,13 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
return 0;
}
+static int platform_msi_alloc_descs(struct device *dev, int nvec,
+ struct platform_msi_priv_data *data)
+
+{
+ return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
+}
+
/**
* platform_msi_create_irq_domain - Create a platform MSI interrupt domain
* @fwnode: Optional fwnode of the interrupt controller
@@ -180,56 +201,75 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
return domain;
}
-/**
- * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
- * @dev: The device for which to allocate interrupts
- * @nvec: The number of interrupts to allocate
- * @write_msi_msg: Callback to write an interrupt message for @dev
- *
- * Returns:
- * Zero for success, or an error code in case of failure
- */
-int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
+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 *priv_data;
- int err;
-
+ struct platform_msi_priv_data *datap;
/*
* Limit the number of interrupts to 256 per device. Should we
* need to bump this up, DEV_ID_SHIFT should be adjusted
* accordingly (which would impact the max number of MSI
* capable devices).
*/
- if (!dev->msi_domain || !write_msi_msg || !nvec ||
- nvec > (1 << (32 - DEV_ID_SHIFT)))
- return -EINVAL;
+ if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
+ return ERR_PTR(-EINVAL);
if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
dev_err(dev, "Incompatible msi_domain, giving up\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
/* Already had a helping of MSI? Greed... */
if (!list_empty(dev_to_msi_list(dev)))
- return -EBUSY;
+ return ERR_PTR(-EBUSY);
+
+ 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);
+ }
- priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
+ datap->write_msg = write_msi_msg;
+ datap->dev = dev;
- priv_data->devid = ida_simple_get(&platform_msi_devid_ida,
- 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
- if (priv_data->devid < 0) {
- err = priv_data->devid;
- goto out_free_data;
- }
+ return datap;
+}
+
+static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
+{
+ ida_simple_remove(&platform_msi_devid_ida, data->devid);
+ kfree(data);
+}
+
+/**
+ * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
+ * @dev: The device for which to allocate interrupts
+ * @nvec: The number of interrupts to allocate
+ * @write_msi_msg: Callback to write an interrupt message for @dev
+ *
+ * Returns:
+ * Zero for success, or an error code in case of failure
+ */
+int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg)
+{
+ struct platform_msi_priv_data *priv_data;
+ int err;
- priv_data->write_msg = write_msi_msg;
+ priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
+ if (IS_ERR(priv_data))
+ return PTR_ERR(priv_data);
err = platform_msi_alloc_descs(dev, nvec, priv_data);
if (err)
- goto out_free_id;
+ goto out_free_priv_data;
err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
if (err)
@@ -238,11 +278,9 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
return 0;
out_free_desc:
- platform_msi_free_descs(dev);
-out_free_id:
- ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
-out_free_data:
- kfree(priv_data);
+ platform_msi_free_descs(dev, 0, nvec);
+out_free_priv_data:
+ platform_msi_free_priv_data(priv_data);
return err;
}
@@ -253,18 +291,126 @@ out_free_data:
*/
void platform_msi_domain_free_irqs(struct device *dev)
{
- struct msi_desc *desc;
+ if (!list_empty(dev_to_msi_list(dev))) {
+ struct msi_desc *desc;
+
+ desc = first_msi_entry(dev);
+ platform_msi_free_priv_data(desc->platform.msi_priv_data);
+ }
+
+ msi_domain_free_irqs(dev->msi_domain, dev);
+ platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
+}
+
+/**
+ * platform_msi_get_host_data - Query the private data associated with
+ * a platform-msi domain
+ * @domain: The platform-msi domain
+ *
+ * Returns the private data provided when calling
+ * platform_msi_create_device_domain.
+ */
+void *platform_msi_get_host_data(struct irq_domain *domain)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ return data->host_data;
+}
+
+/**
+ * platform_msi_create_device_domain - Create a platform-msi domain
+ *
+ * @dev: The device generating the MSIs
+ * @nvec: The number of MSIs that need to be allocated
+ * @write_msi_msg: Callback to write an interrupt message for @dev
+ * @ops: The hierarchy domain operations to use
+ * @host_data: Private data associated to this domain
+ *
+ * Returns an irqdomain for @nvec interrupts
+ */
+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;
+
+ data->host_data = host_data;
+ domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
+ of_node_to_fwnode(dev->of_node),
+ ops, data);
+ if (!domain)
+ goto free_priv;
- desc = first_msi_entry(dev);
- if (desc) {
- struct platform_msi_priv_data *data;
+ err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
+ if (err)
+ goto free_domain;
+
+ return domain;
- data = desc->platform.msi_priv_data;
+free_domain:
+ irq_domain_remove(domain);
+free_priv:
+ platform_msi_free_priv_data(data);
+ return NULL;
+}
+
+/**
+ * platform_msi_domain_free - Free interrupts associated with a platform-msi
+ * domain
+ *
+ * @domain: The platform-msi domain
+ * @virq: The base irq from which to perform the free operation
+ * @nvec: How many interrupts to free from @virq
+ */
+void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nvec)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ struct msi_desc *desc;
+ for_each_msi_entry(desc, data->dev) {
+ if (WARN_ON(!desc->irq || desc->nvec_used != 1))
+ return;
+ if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+ continue;
- ida_simple_remove(&platform_msi_devid_ida, data->devid);
- kfree(data);
+ irq_domain_free_irqs_common(domain, desc->irq, 1);
}
+}
- msi_domain_free_irqs(dev->msi_domain, dev);
- platform_msi_free_descs(dev);
+/**
+ * platform_msi_domain_alloc - Allocate interrupts associated with
+ * a platform-msi domain
+ *
+ * @domain: The platform-msi domain
+ * @virq: The base irq from which to perform the allocate operation
+ * @nvec: How many interrupts to free from @virq
+ *
+ * Return 0 on success, or an error code on failure. Must be called
+ * with irq_domain_mutex held (which can only be done as part of a
+ * top-level interrupt allocation).
+ */
+int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ int err;
+
+ err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
+ if (err)
+ return err;
+
+ err = msi_domain_populate_irqs(domain->parent, data->dev,
+ virq, nr_irqs, &data->arg);
+ if (err)
+ platform_msi_domain_free(domain, virq, nr_irqs);
+
+ return err;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index d77ed0c946dd..8dcbb266643b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -118,6 +118,26 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
EXPORT_SYMBOL_GPL(platform_get_irq);
/**
+ * platform_irq_count - Count the number of IRQs a platform device uses
+ * @dev: platform device
+ *
+ * Return: Number of IRQs a platform device uses or EPROBE_DEFER
+ */
+int platform_irq_count(struct platform_device *dev)
+{
+ int ret, nr = 0;
+
+ while ((ret = platform_get_irq(dev, nr)) >= 0)
+ nr++;
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return nr;
+}
+EXPORT_SYMBOL_GPL(platform_irq_count);
+
+/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index 0246f44ded74..686c9e0b930e 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -21,7 +21,7 @@ static int regcache_flat_init(struct regmap *map)
int i;
unsigned int *cache;
- map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
+ map->cache = kcalloc(map->max_register + 1, sizeof(unsigned int),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
index 736e0d378567..6f77d7319fc6 100644
--- a/drivers/base/regmap/regcache-lzo.c
+++ b/drivers/base/regmap/regcache-lzo.c
@@ -139,7 +139,7 @@ static int regcache_lzo_init(struct regmap *map)
ret = 0;
blkcount = regcache_lzo_block_count(map);
- map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
+ map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
@@ -152,8 +152,8 @@ static int regcache_lzo_init(struct regmap *map)
* that register.
*/
bmp_size = map->num_reg_defaults_raw;
- sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
- GFP_KERNEL);
+ sync_bmp = kmalloc_array(BITS_TO_LONGS(bmp_size), sizeof(long),
+ GFP_KERNEL);
if (!sync_bmp) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index 56486d92c4e7..aa56af87d941 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -361,13 +361,14 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
rbnode->base_reg = reg;
}
- rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
- GFP_KERNEL);
+ rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
+ GFP_KERNEL);
if (!rbnode->block)
goto err_free;
- rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
- sizeof(*rbnode->cache_present), GFP_KERNEL);
+ rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
+ sizeof(*rbnode->cache_present),
+ GFP_KERNEL);
if (!rbnode->cache_present)
goto err_free_block;
@@ -413,8 +414,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
max = reg + max_dist;
/* look for an adjacent register to the one we are about to add */
- for (node = rb_first(&rbtree_ctx->root); node;
- node = rb_next(node)) {
+ node = rbtree_ctx->root.rb_node;
+ while (node) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
@@ -425,6 +426,11 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg);
} else {
+ if (max < base_reg)
+ node = node->rb_left;
+ else
+ node = node->rb_right;
+
continue;
}
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 4c07802986b2..348be3a35410 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -100,15 +100,25 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
int i;
void *tmp_buf;
- for (i = 0; i < config->num_reg_defaults; i++)
- if (config->reg_defaults[i].reg % map->reg_stride)
- return -EINVAL;
-
if (map->cache_type == REGCACHE_NONE) {
+ if (config->reg_defaults || config->num_reg_defaults_raw)
+ dev_warn(map->dev,
+ "No cache used with register defaults set!\n");
+
map->cache_bypass = true;
return 0;
}
+ if (config->reg_defaults && !config->num_reg_defaults) {
+ dev_err(map->dev,
+ "Register defaults are set without the number!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < config->num_reg_defaults; i++)
+ if (config->reg_defaults[i].reg % map->reg_stride)
+ return -EINVAL;
+
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
if (cache_types[i]->type == map->cache_type)
break;
@@ -138,8 +148,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* a copy of it.
*/
if (config->reg_defaults) {
- if (!map->num_reg_defaults)
- return -EINVAL;
tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
sizeof(struct reg_default), GFP_KERNEL);
if (!tmp_buf)
@@ -535,19 +543,30 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
switch (map->cache_word_size) {
case 1: {
u8 *cache = base;
+
cache[idx] = val;
break;
}
case 2: {
u16 *cache = base;
+
cache[idx] = val;
break;
}
case 4: {
u32 *cache = base;
+
+ cache[idx] = val;
+ break;
+ }
+#ifdef CONFIG_64BIT
+ case 8: {
+ u64 *cache = base;
+
cache[idx] = val;
break;
}
+#endif
default:
BUG();
}
@@ -568,16 +587,26 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
switch (map->cache_word_size) {
case 1: {
const u8 *cache = base;
+
return cache[idx];
}
case 2: {
const u16 *cache = base;
+
return cache[idx];
}
case 4: {
const u32 *cache = base;
+
+ return cache[idx];
+ }
+#ifdef CONFIG_64BIT
+ case 8: {
+ const u64 *cache = base;
+
return cache[idx];
}
+#endif
default:
BUG();
}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 3f0a7e262d69..1ee3d40861c7 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -397,72 +397,39 @@ static const struct file_operations regmap_reg_ranges_fops = {
.llseek = default_llseek,
};
-static ssize_t regmap_access_read_file(struct file *file,
-