diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/core.c | 129 | ||||
-rw-r--r-- | drivers/base/power/common.c | 21 | ||||
-rw-r--r-- | drivers/base/power/qos.c | 1 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-maple.c | 3 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 9 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-kunit.c | 45 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 1 |
8 files changed, 176 insertions, 34 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 048ff98dbdfd..529af59e25ff 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5012,6 +5012,49 @@ define_dev_printk_level(_dev_info, KERN_INFO); #endif +static void __dev_probe_failed(const struct device *dev, int err, bool fatal, + const char *fmt, va_list vargsp) +{ + struct va_format vaf; + va_list vargs; + + /* + * On x86_64 and possibly on other architectures, va_list is actually a + * size-1 array containing a structure. As a result, function parameter + * vargsp decays from T[1] to T*, and &vargsp has type T** rather than + * T(*)[1], which is expected by its assignment to vaf.va below. + * + * One standard way to solve this mess is by creating a copy in a local + * variable of type va_list and then using a pointer to that local copy + * instead, which is the approach employed here. + */ + va_copy(vargs, vargsp); + + vaf.fmt = fmt; + vaf.va = &vargs; + + switch (err) { + case -EPROBE_DEFER: + device_set_deferred_probe_reason(dev, &vaf); + dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + break; + + case -ENOMEM: + /* Don't print anything on -ENOMEM, there's already enough output */ + break; + + default: + /* Log fatal final failures as errors, otherwise produce warnings */ + if (fatal) + dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + else + dev_warn(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + break; + } + + va_end(vargs); +} + /** * dev_err_probe - probe error check and log helper * @dev: the pointer to the struct device @@ -5024,7 +5067,7 @@ define_dev_printk_level(_dev_info, KERN_INFO); * -EPROBE_DEFER and propagate error upwards. * In case of -EPROBE_DEFER it sets also defer probe reason, which can be * checked later by reading devices_deferred debugfs attribute. - * It replaces code sequence:: + * It replaces the following code sequence:: * * if (err != -EPROBE_DEFER) * dev_err(dev, ...); @@ -5036,47 +5079,77 @@ define_dev_printk_level(_dev_info, KERN_INFO); * * return dev_err_probe(dev, err, ...); * - * Using this helper in your probe function is totally fine even if @err is - * known to never be -EPROBE_DEFER. + * Using this helper in your probe function is totally fine even if @err + * is known to never be -EPROBE_DEFER. * The benefit compared to a normal dev_err() is the standardized format - * of the error code, it being emitted symbolically (i.e. you get "EAGAIN" - * instead of "-35") and the fact that the error code is returned which allows - * more compact error paths. + * of the error code, which is emitted symbolically (i.e. you get "EAGAIN" + * instead of "-35"), and having the error code returned allows more + * compact error paths. * * Returns @err. */ int dev_err_probe(const struct device *dev, int err, const char *fmt, ...) { - struct va_format vaf; - va_list args; + va_list vargs; - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; + va_start(vargs, fmt); - switch (err) { - case -EPROBE_DEFER: - device_set_deferred_probe_reason(dev, &vaf); - dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf); - break; + /* Use dev_err() for logging when err doesn't equal -EPROBE_DEFER */ + __dev_probe_failed(dev, err, true, fmt, vargs); - case -ENOMEM: - /* - * We don't print anything on -ENOMEM, there is already enough - * output. - */ - break; + va_end(vargs); - default: - dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf); - break; - } + return err; +} +EXPORT_SYMBOL_GPL(dev_err_probe); - va_end(args); +/** + * dev_warn_probe - probe error check and log helper + * @dev: the pointer to the struct device + * @err: error value to test + * @fmt: printf-style format string + * @...: arguments as specified in the format string + * + * This helper implements common pattern present in probe functions for error + * checking: print debug or warning message depending if the error value is + * -EPROBE_DEFER and propagate error upwards. + * In case of -EPROBE_DEFER it sets also defer probe reason, which can be + * checked later by reading devices_deferred debugfs attribute. + * It replaces the following code sequence:: + * + * if (err != -EPROBE_DEFER) + * dev_warn(dev, ...); + * else + * dev_dbg(dev, ...); + * return err; + * + * with:: + * + * return dev_warn_probe(dev, err, ...); + * + * Using this helper in your probe function is totally fine even if @err + * is known to never be -EPROBE_DEFER. + * The benefit compared to a normal dev_warn() is the standardized format + * of the error code, which is emitted symbolically (i.e. you get "EAGAIN" + * instead of "-35"), and having the error code returned allows more + * compact error paths. + * + * Returns @err. + */ +int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...) +{ + va_list vargs; + + va_start(vargs, fmt); + + /* Use dev_warn() for logging when err doesn't equal -EPROBE_DEFER */ + __dev_probe_failed(dev, err, false, fmt, vargs); + + va_end(vargs); return err; } -EXPORT_SYMBOL_GPL(dev_err_probe); +EXPORT_SYMBOL_GPL(dev_warn_probe); static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) { diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index cca2fd0a1aed..781968a128ff 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -11,6 +11,7 @@ #include <linux/pm_clock.h> #include <linux/acpi.h> #include <linux/pm_domain.h> +#include <linux/pm_opp.h> #include "power.h" @@ -222,13 +223,15 @@ int dev_pm_domain_attach_list(struct device *dev, if (!pds) return -ENOMEM; - size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links); + size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links) + + sizeof(*pds->opp_tokens); pds->pd_devs = kcalloc(num_pds, size, GFP_KERNEL); if (!pds->pd_devs) { ret = -ENOMEM; goto free_pds; } pds->pd_links = (void *)(pds->pd_devs + num_pds); + pds->opp_tokens = (void *)(pds->pd_links + num_pds); if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON) link_flags |= DL_FLAG_RPM_ACTIVE; @@ -244,6 +247,19 @@ int dev_pm_domain_attach_list(struct device *dev, goto err_attach; } + if (pd_flags & PD_FLAG_REQUIRED_OPP) { + struct dev_pm_opp_config config = { + .required_dev = pd_dev, + .required_dev_index = i, + }; + + ret = dev_pm_opp_set_config(dev, &config); + if (ret < 0) + goto err_link; + + pds->opp_tokens[i] = ret; + } + if (link_flags) { struct device_link *link; @@ -264,9 +280,11 @@ int dev_pm_domain_attach_list(struct device *dev, return num_pds; err_link: + dev_pm_opp_clear_config(pds->opp_tokens[i]); dev_pm_domain_detach(pd_dev, true); err_attach: while (--i >= 0) { + dev_pm_opp_clear_config(pds->opp_tokens[i]); if (pds->pd_links[i]) device_link_del(pds->pd_links[i]); dev_pm_domain_detach(pds->pd_devs[i], true); @@ -361,6 +379,7 @@ void dev_pm_domain_detach_list(struct dev_pm_domain_list *list) return; for (i = 0; i < list->num_pds; i++) { + dev_pm_opp_clear_config(list->opp_tokens[i]); if (list->pd_links[i]) device_link_del(list->pd_links[i]); dev_pm_domain_detach(list->pd_devs[i], true); diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index bd77f6734f14..ff393cba7649 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -137,6 +137,7 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) return ret; } +EXPORT_SYMBOL_GPL(dev_pm_qos_read_value); /** * apply_constraint - Add/modify/remove device PM QoS request. diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 83acccdc1008..bdb450436cbc 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -59,6 +59,7 @@ struct regmap { unsigned long raw_spinlock_flags; }; }; + struct lock_class_key *lock_key; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index 8d27d3653ea3..23da7b31d715 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -355,6 +355,9 @@ static int regcache_maple_init(struct regmap *map) mt_init(mt); + if (!mt_external_lock(mt) && map->lock_key) + lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); + if (!map->num_reg_defaults) return 0; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index a750e48a26b8..0bcd81389a29 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -364,14 +364,11 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) memset32(data->status_buf, GENMASK(31, 0), chip->num_regs); } else if (chip->num_main_regs) { unsigned int max_main_bits; - unsigned long size; - - size = chip->num_regs * sizeof(unsigned int); max_main_bits = (chip->num_main_status_bits) ? chip->num_main_status_bits : chip->num_regs; /* Clear the status buf as we don't read all status regs */ - memset(data->status_buf, 0, size); + memset32(data->status_buf, 0, chip->num_regs); /* We could support bulk read for main status registers * but I don't expect to see devices with really many main @@ -514,12 +511,16 @@ exit: return IRQ_NONE; } +static struct lock_class_key regmap_irq_lock_class; +static struct lock_class_key regmap_irq_request_class; + static int regmap_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { struct regmap_irq_chip_data *data = h->host_data; irq_set_chip_data(virq, data); + irq_set_lockdep_class(virq, ®map_irq_lock_class, ®map_irq_request_class); irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); irq_set_parent(virq, data->irq); diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 4bf3f1e59ed7..64ea340950b6 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -126,7 +126,7 @@ static const struct regmap_test_param real_cache_types_list[] = { { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, { .cache = REGCACHE_MAPLE, .from_reg = 0 }, - { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, @@ -1499,6 +1499,48 @@ static void cache_present(struct kunit *test) KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, param->from_reg + i)); } +static void cache_write_zero(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[param->from_reg + i] = false; + + /* No defaults so no registers cached. */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, param->from_reg + i)); + + /* We didn't trigger any reads */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_ASSERT_FALSE(test, data->read[param->from_reg + i]); + + /* Write a zero value */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 1, 0)); + + /* Read that zero value back */ + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 1, &val)); + KUNIT_EXPECT_EQ(test, 0, val); + + /* From the cache? */ + KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, 1)); + + /* Try to throw it away */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 1, 1)); + KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1)); +} + /* Check that caching the window register works with sync */ static void cache_range_window_reg(struct kunit *test) { @@ -2012,6 +2054,7 @@ static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(cache_drop_all_and_sync_no_defaults, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params), KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params), diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4ded93687c1f..53131a7ede0a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -745,6 +745,7 @@ struct regmap *__regmap_init(struct device *dev, lock_key, lock_name); } map->lock_arg = map; + map->lock_key = lock_key; } /* |