diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/node.c | 18 | ||||
-rw-r--r-- | drivers/base/power/domain.c | 64 | ||||
-rw-r--r-- | drivers/base/power/domain_governor.c | 1 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 18 | ||||
-rw-r--r-- | drivers/base/power/wakeirq.c | 4 | ||||
-rw-r--r-- | drivers/base/property.c | 31 | ||||
-rw-r--r-- | drivers/base/regmap/Kconfig | 6 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-i2c.c | 45 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 7 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-mdio.c | 116 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 15 | ||||
-rw-r--r-- | drivers/base/swnode.c | 16 |
13 files changed, 278 insertions, 64 deletions
diff --git a/drivers/base/node.c b/drivers/base/node.c index 2c36f61d30bc..9db297431b97 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -482,6 +482,7 @@ static DEVICE_ATTR(meminfo, 0444, node_read_meminfo, NULL); static ssize_t node_read_numastat(struct device *dev, struct device_attribute *attr, char *buf) { + fold_vm_numa_events(); return sysfs_emit(buf, "numa_hit %lu\n" "numa_miss %lu\n" @@ -489,12 +490,12 @@ static ssize_t node_read_numastat(struct device *dev, "interleave_hit %lu\n" "local_node %lu\n" "other_node %lu\n", - sum_zone_numa_state(dev->id, NUMA_HIT), - sum_zone_numa_state(dev->id, NUMA_MISS), - sum_zone_numa_state(dev->id, NUMA_FOREIGN), - sum_zone_numa_state(dev->id, NUMA_INTERLEAVE_HIT), - sum_zone_numa_state(dev->id, NUMA_LOCAL), - sum_zone_numa_state(dev->id, NUMA_OTHER)); + sum_zone_numa_event_state(dev->id, NUMA_HIT), + sum_zone_numa_event_state(dev->id, NUMA_MISS), + sum_zone_numa_event_state(dev->id, NUMA_FOREIGN), + sum_zone_numa_event_state(dev->id, NUMA_INTERLEAVE_HIT), + sum_zone_numa_event_state(dev->id, NUMA_LOCAL), + sum_zone_numa_event_state(dev->id, NUMA_OTHER)); } static DEVICE_ATTR(numastat, 0444, node_read_numastat, NULL); @@ -512,10 +513,11 @@ static ssize_t node_read_vmstat(struct device *dev, sum_zone_node_page_state(nid, i)); #ifdef CONFIG_NUMA - for (i = 0; i < NR_VM_NUMA_STAT_ITEMS; i++) + fold_vm_numa_events(); + for (i = 0; i < NR_VM_NUMA_EVENT_ITEMS; i++) len += sysfs_emit_at(buf, len, "%s %lu\n", numa_stat_name(i), - sum_zone_numa_state(nid, i)); + sum_zone_numa_event_state(nid, i)); #endif for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) { diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b6a782c31613..ab0b740cc0f1 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -379,6 +379,44 @@ err: return ret; } +static int genpd_set_performance_state(struct device *dev, unsigned int state) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); + unsigned int prev_state; + int ret; + + prev_state = gpd_data->performance_state; + if (prev_state == state) + return 0; + + gpd_data->performance_state = state; + state = _genpd_reeval_performance_state(genpd, state); + + ret = _genpd_set_performance_state(genpd, state, 0); + if (ret) + gpd_data->performance_state = prev_state; + + return ret; +} + +static int genpd_drop_performance_state(struct device *dev) +{ + unsigned int prev_state = dev_gpd_data(dev)->performance_state; + + if (!genpd_set_performance_state(dev, 0)) + return prev_state; + + return 0; +} + +static void genpd_restore_performance_state(struct device *dev, + unsigned int state) +{ + if (state) + genpd_set_performance_state(dev, state); +} + /** * dev_pm_genpd_set_performance_state- Set performance state of device's power * domain. @@ -397,8 +435,6 @@ err: int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) { struct generic_pm_domain *genpd; - struct generic_pm_domain_data *gpd_data; - unsigned int prev; int ret; genpd = dev_to_genpd_safe(dev); @@ -410,16 +446,7 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) return -EINVAL; genpd_lock(genpd); - - gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - prev = gpd_data->performance_state; - gpd_data->performance_state = state; - - state = _genpd_reeval_performance_state(genpd, state); - ret = _genpd_set_performance_state(genpd, state, 0); - if (ret) - gpd_data->performance_state = prev; - + ret = genpd_set_performance_state(dev, state); genpd_unlock(genpd); return ret; @@ -572,6 +599,7 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) * RPM status of the releated device is in an intermediate state, not yet turned * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not * be RPM_SUSPENDED, while it tries to power off the PM domain. + * @depth: nesting count for lockdep. * * If all of the @genpd's devices have been suspended and all of its subdomains * have been powered down, remove power from @genpd. @@ -832,7 +860,8 @@ static int genpd_runtime_suspend(struct device *dev) { struct generic_pm_domain *genpd; bool (*suspend_ok)(struct device *__dev); - struct gpd_timing_data *td = &dev_gpd_data(dev)->td; + struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); + struct gpd_timing_data *td = &gpd_data->td; bool runtime_pm = pm_runtime_enabled(dev); ktime_t time_start; s64 elapsed_ns; @@ -889,6 +918,7 @@ static int genpd_runtime_suspend(struct device *dev) return 0; genpd_lock(genpd); + gpd_data->rpm_pstate = genpd_drop_performance_state(dev); genpd_power_off(genpd, true, 0); genpd_unlock(genpd); @@ -906,7 +936,8 @@ static int genpd_runtime_suspend(struct device *dev) static int genpd_runtime_resume(struct device *dev) { struct generic_pm_domain *genpd; - struct gpd_timing_data *td = &dev_gpd_data(dev)->td; + struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); + struct gpd_timing_data *td = &gpd_data->td; bool runtime_pm = pm_runtime_enabled(dev); ktime_t time_start; s64 elapsed_ns; @@ -930,6 +961,8 @@ static int genpd_runtime_resume(struct device *dev) genpd_lock(genpd); ret = genpd_power_on(genpd, 0); + if (!ret) + genpd_restore_performance_state(dev, gpd_data->rpm_pstate); genpd_unlock(genpd); if (ret) @@ -968,6 +1001,7 @@ err_stop: err_poweroff: if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { genpd_lock(genpd); + gpd_data->rpm_pstate = genpd_drop_performance_state(dev); genpd_power_off(genpd, true, 0); genpd_unlock(genpd); } @@ -2505,7 +2539,7 @@ EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain); /** * of_genpd_remove_last - Remove the last PM domain registered for a provider - * @provider: Pointer to device structure associated with provider + * @np: Pointer to device node associated with provider * * Find the last PM domain that was added by a particular provider and * remove this PM domain from the list of PM domains. The provider is diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index c6c218758f0b..cd08c5885190 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -252,6 +252,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, /** * _default_power_down_ok - Default generic PM domain power off governor routine. * @pd: PM domain to check. + * @now: current ktime. * * This routine must be executed under the PM domain's lock. */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b570848d23e0..8a66eaf731e4 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -345,7 +345,7 @@ static void rpm_suspend_suppliers(struct device *dev) static int __rpm_callback(int (*cb)(struct device *), struct device *dev) __releases(&dev->power.lock) __acquires(&dev->power.lock) { - int retval, idx; + int retval = 0, idx; bool use_links = dev->power.links_count > 0; if (dev->power.irq_safe) { @@ -373,7 +373,8 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev) } } - retval = cb(dev); + if (cb) + retval = cb(dev); if (dev->power.irq_safe) { spin_lock(&dev->power.lock); @@ -446,7 +447,10 @@ static int rpm_idle(struct device *dev, int rpmflags) /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; - if (dev->power.no_callbacks) + callback = RPM_GET_CALLBACK(dev, runtime_idle); + + /* If no callback assume success. */ + if (!callback || dev->power.no_callbacks) goto out; /* Carry out an asynchronous or a synchronous idle notification. */ @@ -462,10 +466,7 @@ static int rpm_idle(struct device *dev, int rpmflags) dev->power.idle_notification = true; - callback = RPM_GET_CALLBACK(dev, runtime_idle); - - if (callback) - retval = __rpm_callback(callback, dev); + retval = __rpm_callback(callback, dev); dev->power.idle_notification = false; wake_up_all(&dev->power.wait_queue); @@ -484,9 +485,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) { int retval; - if (!cb) - return -ENOSYS; - if (dev->power.memalloc_noio) { unsigned int noio_flag; diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 8e021082dba8..3bad3266a2ad 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -182,7 +182,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) wirq->dev = dev; wirq->irq = irq; - irq_set_status_flags(irq, IRQ_NOAUTOEN); /* Prevent deferred spurious wakeirqs with disable_irq_nosync() */ irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); @@ -192,7 +191,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) * so we use a threaded irq. */ err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq, - IRQF_ONESHOT, wirq->name, wirq); + IRQF_ONESHOT | IRQF_NO_AUTOEN, + wirq->name, wirq); if (err) goto err_free_name; diff --git a/drivers/base/property.c b/drivers/base/property.c index 1421e9548857..1f533b314efc 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -21,7 +21,7 @@ struct fwnode_handle *dev_fwnode(struct device *dev) { return IS_ENABLED(CONFIG_OF) && dev->of_node ? - &dev->of_node->fwnode : dev->fwnode; + of_fwnode_handle(dev->of_node) : dev->fwnode; } EXPORT_SYMBOL_GPL(dev_fwnode); @@ -759,13 +759,8 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_available_child_node); struct fwnode_handle *device_get_next_child_node(struct device *dev, struct fwnode_handle *child) { - struct acpi_device *adev = ACPI_COMPANION(dev); - struct fwnode_handle *fwnode = NULL, *next; - - if (dev->of_node) - fwnode = &dev->of_node->fwnode; - else if (adev) - fwnode = acpi_fwnode_handle(adev); + const struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_handle *next; /* Try to find a child in primary fwnode */ next = fwnode_get_next_child_node(fwnode, child); @@ -868,28 +863,31 @@ EXPORT_SYMBOL_GPL(device_get_child_node_count); bool device_dma_supported(struct device *dev) { + const struct fwnode_handle *fwnode = dev_fwnode(dev); + /* For DT, this is always supported. * For ACPI, this depends on CCA, which * is determined by the acpi_dma_supported(). */ - if (IS_ENABLED(CONFIG_OF) && dev->of_node) + if (is_of_node(fwnode)) return true; - return acpi_dma_supported(ACPI_COMPANION(dev)); + return acpi_dma_supported(to_acpi_device_node(fwnode)); } EXPORT_SYMBOL_GPL(device_dma_supported); enum dev_dma_attr device_get_dma_attr(struct device *dev) { + const struct fwnode_handle *fwnode = dev_fwnode(dev); enum dev_dma_attr attr = DEV_DMA_NOT_SUPPORTED; - if (IS_ENABLED(CONFIG_OF) && dev->of_node) { - if (of_dma_is_coherent(dev->of_node)) + if (is_of_node(fwnode)) { + if (of_dma_is_coherent(to_of_node(fwnode))) attr = DEV_DMA_COHERENT; else attr = DEV_DMA_NON_COHERENT; } else - attr = acpi_get_dma_attr(ACPI_COMPANION(dev)); + attr = acpi_get_dma_attr(to_acpi_device_node(fwnode)); return attr; } @@ -1007,14 +1005,13 @@ EXPORT_SYMBOL(device_get_mac_address); * Returns Linux IRQ number on success. Other values are determined * accordingly to acpi_/of_ irq_get() operation. */ -int fwnode_irq_get(struct fwnode_handle *fwnode, unsigned int index) +int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index) { - struct device_node *of_node = to_of_node(fwnode); struct resource res; int ret; - if (IS_ENABLED(CONFIG_OF) && of_node) - return of_irq_get(of_node, index); + if (is_of_node(fwnode)) + return of_irq_get(to_of_node(fwnode), index); ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res); if (ret) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 50b1e2d06a25..159bac6c5046 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -4,8 +4,9 @@ # subsystems should select the appropriate symbols. config REGMAP - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO) select IRQ_DOMAIN if REGMAP_IRQ + select MDIO_BUS if REGMAP_MDIO bool config REGCACHE_COMPRESSED @@ -36,6 +37,9 @@ config REGMAP_W1 tristate depends on W1 +config REGMAP_MDIO + tristate + config REGMAP_MMIO tristate diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 33f63adb5b3d..11facb32a027 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_REGMAP_SOUNDWIRE_MBQ) += regmap-sdw-mbq.o obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o +obj-$(CONFIG_REGMAP_MDIO) += regmap-mdio.o diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 62b95a9212ae..980e5ce6a3a3 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -306,33 +306,64 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = { static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, const struct regmap_config *config) { + const struct i2c_adapter_quirks *quirks; + const struct regmap_bus *bus = NULL; + struct regmap_bus *ret_bus; + u16 max_read = 0, max_write = 0; + if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) - return ®map_i2c; + bus = ®map_i2c; else if (config->val_bits == 8 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) - return ®map_i2c_smbus_i2c_block; + bus = ®map_i2c_smbus_i2c_block; else if (config->val_bits == 8 && config->reg_bits == 16 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) - return ®map_i2c_smbus_i2c_block_reg16; + bus = ®map_i2c_smbus_i2c_block_reg16; else if (config->val_bits == 16 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) switch (regmap_get_val_endian(&i2c->dev, NULL, config)) { case REGMAP_ENDIAN_LITTLE: - return ®map_smbus_word; + bus = ®map_smbus_word; + break; case REGMAP_ENDIAN_BIG: - return ®map_smbus_word_swapped; + bus = ®map_smbus_word_swapped; + break; default: /* everything else is not supported */ break; } else if (config->val_bits == 8 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return ®map_smbus_byte; + bus = ®map_smbus_byte; + + if (!bus) + return ERR_PTR(-ENOTSUPP); + + quirks = i2c->adapter->quirks; + if (quirks) { + if (quirks->max_read_len && + (bus->max_raw_read == 0 || bus->max_raw_read > quirks->max_read_len)) + max_read = quirks->max_read_len; + + if (quirks->max_write_len && + (bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len)) + max_write = quirks->max_write_len; + + if (max_read || max_write) { + ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL); + if (!ret_bus) + return ERR_PTR(-ENOMEM); + ret_bus->free_on_exit = true; + ret_bus->max_raw_read = max_read; + ret_bus->max_raw_write = max_write; + bus = ret_bus; + } + } - return ERR_PTR(-ENOTSUPP); + return bus; } struct regmap *__regmap_init_i2c(struct i2c_client *i2c, diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 760296a4b606..d2656581a608 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -531,6 +531,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } + if (chip->status_invert) + for (i = 0; i < data->chip->num_regs; i++) + data->status_buf[i] = ~data->status_buf[i]; + /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowleding the @@ -800,6 +804,9 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, goto err_alloc; } + if (chip->status_invert) + d->status_buf[i] = ~d->status_buf[i]; + if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { reg = sub_irq_reg(d, d->chip->ack_base, i); if (chip->ack_invert) diff --git a/drivers/base/regmap/regmap-mdio.c b/drivers/base/regmap/regmap-mdio.c new file mode 100644 index 000000000000..6a20201299f5 --- /dev/null +++ b/drivers/base/regmap/regmap-mdio.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/errno.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#define REGVAL_MASK GENMASK(15, 0) +#define REGNUM_C22_MASK GENMASK(4, 0) +/* Clause-45 mask includes the device type (5 bit) and actual register number (16 bit) */ +#define REGNUM_C45_MASK GENMASK(20, 0) + +static int regmap_mdio_read(struct mdio_device *mdio_dev, u32 reg, unsigned int *val) +{ + int ret; + + ret = mdiobus_read(mdio_dev->bus, mdio_dev->addr, reg); + if (ret < 0) + return ret; + + *val = ret & REGVAL_MASK; + return 0; +} + +static int regmap_mdio_write(struct mdio_device *mdio_dev, u32 reg, unsigned int val) +{ + return mdiobus_write(mdio_dev->bus, mdio_dev->addr, reg, val); +} + +static int regmap_mdio_c22_read(void *context, unsigned int reg, unsigned int *val) +{ + struct mdio_device *mdio_dev = context; + + if (unlikely(reg & ~REGNUM_C22_MASK)) + return -ENXIO; + + return regmap_mdio_read(mdio_dev, reg, val); +} + +static int regmap_mdio_c22_write(void *context, unsigned int reg, unsigned int val) +{ + struct mdio_device *mdio_dev = context; + + if (unlikely(reg & ~REGNUM_C22_MASK)) + return -ENXIO; + + return mdiobus_write(mdio_dev->bus, mdio_dev->addr, reg, val); +} + +static const struct regmap_bus regmap_mdio_c22_bus = { + .reg_write = regmap_mdio_c22_write, + .reg_read = regmap_mdio_c22_read, +}; + +static int regmap_mdio_c45_read(void *context, unsigned int reg, unsigned int *val) +{ + struct mdio_device *mdio_dev = context; + + if (unlikely(reg & ~REGNUM_C45_MASK)) + return -ENXIO; + + return regmap_mdio_read(mdio_dev, MII_ADDR_C45 | reg, val); +} + +static int regmap_mdio_c45_write(void *context, unsigned int reg, unsigned int val) +{ + struct mdio_device *mdio_dev = context; + + if (unlikely(reg & ~REGNUM_C45_MASK)) + return -ENXIO; + + return regmap_mdio_write(mdio_dev, MII_ADDR_C45 | reg, val); +} + +static const struct regmap_bus regmap_mdio_c45_bus = { + .reg_write = regmap_mdio_c45_write, + .reg_read = regmap_mdio_c45_read, +}; + +struct regmap *__regmap_init_mdio(struct mdio_device *mdio_dev, + const struct regmap_config *config, struct lock_class_key *lock_key, + const char *lock_name) +{ + const struct regmap_bus *bus; + + if (config->reg_bits == 5 && config->val_bits == 16) + bus = ®map_mdio_c22_bus; + else if (config->reg_bits == 21 && config->val_bits == 16) + bus = ®map_mdio_c45_bus; + else + return ERR_PTR(-EOPNOTSUPP); + + return __regmap_init(&mdio_dev->dev, bus, mdio_dev, config, lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__regmap_init_mdio); + +struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev, + const struct regmap_config *config, struct lock_class_key *lock_key, + const char *lock_name) +{ + const struct regmap_bus *bus; + + if (config->reg_bits == 5 && config->val_bits == 16) + bus = ®map_mdio_c22_bus; + else if (config->reg_bits == 21 && config->val_bits == 16) + bus = ®map_mdio_c45_bus; + else + return ERR_PTR(-EOPNOTSUPP); + + return __devm_regmap_init(&mdio_dev->dev, bus, mdio_dev, config, lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__devm_regmap_init_mdio); + +MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>"); +MODULE_DESCRIPTION("Regmap MDIO Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 297e95be25b3..fe3e38dd5324 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -243,6 +243,16 @@ static void regmap_format_7_9_write(struct regmap *map, *out = cpu_to_be16((reg << 9) | val); } +static void regmap_format_7_17_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + out[2] = val; + out[1] = val >> 8; + out[0] = (val >> 16) | (reg << 1); +} + static void regmap_format_10_14_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -885,6 +895,9 @@ struct regmap *__regmap_init(struct device *dev, case 9: map->format.format_write = regmap_format_7_9_write; break; + case 17: + map->format.format_write = regmap_format_7_17_write; + break; default: goto err_hwlock; } @@ -1496,6 +1509,8 @@ void regmap_exit(struct regmap *map) mutex_destroy(&map->mutex); kfree_const(map->name); kfree(map->patch); + if (map->bus && map->bus->free_on_exit) + kfree(map->bus); kfree(map); } EXPORT_SYMBOL_GPL(regmap_exit); diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 3cc11b813f28..d1f1a8240120 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -1045,7 +1045,15 @@ int device_add_software_node(struct device *dev, const struct software_node *nod } set_secondary_fwnode(dev, &swnode->fwnode); - software_node_notify(dev, KOBJ_ADD); + + /* + * If the device has been fully registered by the time this function is + * called, software_node_notify() must be called separately so that the + * symlinks get created and the reference count of the node is kept in + * balance. + */ + if (device_is_registered(dev)) + software_node_notify(dev, KOBJ_ADD); return 0; } @@ -1065,7 +1073,8 @@ void device_remove_software_node(struct device *dev) if (!swnode) return; - software_node_notify(dev, KOBJ_REMOVE); + if (device_is_registered(dev)) + software_node_notify(dev, KOBJ_REMOVE); set_secondary_fwnode(dev, NULL); kobject_put(&swnode->kobj); } @@ -1119,8 +1128,7 @@ int software_node_notify(struct device *dev, unsigned long action) switch (action) { case KOBJ_ADD: - ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj, - "software_node"); + ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node"); if (ret) break; |