diff options
-rw-r--r-- | Documentation/devicetree/bindings/eeprom/at24.yaml | 9 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/eeprom/at25.txt | 2 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/nvmem/nvmem.yaml | 11 | ||||
-rw-r--r-- | drivers/misc/eeprom/at24.c | 72 | ||||
-rw-r--r-- | drivers/nvmem/core.c | 19 | ||||
-rw-r--r-- | drivers/nvmem/nvmem.h | 2 | ||||
-rw-r--r-- | include/linux/nvmem-provider.h | 3 |
7 files changed, 91 insertions, 27 deletions
diff --git a/Documentation/devicetree/bindings/eeprom/at24.yaml b/Documentation/devicetree/bindings/eeprom/at24.yaml index e8778560d966..0f6d8db18d6c 100644 --- a/Documentation/devicetree/bindings/eeprom/at24.yaml +++ b/Documentation/devicetree/bindings/eeprom/at24.yaml @@ -145,10 +145,7 @@ properties: over reads to the next slave address. Please consult the manual of your device. - wp-gpios: - description: - GPIO to which the write-protect pin of the chip is connected. - maxItems: 1 + wp-gpios: true address-width: allOf: @@ -167,6 +164,10 @@ properties: minimum: 1 maximum: 8 + vcc-supply: + description: + phandle of the regulator that provides the supply voltage. + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/eeprom/at25.txt b/Documentation/devicetree/bindings/eeprom/at25.txt index 42577dd113dd..fcacd97abd0a 100644 --- a/Documentation/devicetree/bindings/eeprom/at25.txt +++ b/Documentation/devicetree/bindings/eeprom/at25.txt @@ -20,6 +20,7 @@ Optional properties: - spi-cpha : SPI shifted clock phase, as per spi-bus bindings. - spi-cpol : SPI inverse clock polarity, as per spi-bus bindings. - read-only : this parameter-less property disables writes to the eeprom +- wp-gpios : GPIO to which the write-protect pin of the chip is connected Obsolete legacy properties can be used in place of "size", "pagesize", "address-width", and "read-only": @@ -36,6 +37,7 @@ Example: spi-max-frequency = <5000000>; spi-cpha; spi-cpol; + wp-gpios = <&gpio1 3 0>; pagesize = <64>; size = <32768>; diff --git a/Documentation/devicetree/bindings/nvmem/nvmem.yaml b/Documentation/devicetree/bindings/nvmem/nvmem.yaml index 1c75a059206c..b43c6c65294e 100644 --- a/Documentation/devicetree/bindings/nvmem/nvmem.yaml +++ b/Documentation/devicetree/bindings/nvmem/nvmem.yaml @@ -34,6 +34,14 @@ properties: description: Mark the provider as read only. + wp-gpios: + description: + GPIO to which the write-protect pin of the chip is connected. + The write-protect GPIO is asserted, when it's driven high + (logical '1') to block the write operation. It's deasserted, + when it's driven low (logical '0') to allow writing. + maxItems: 1 + patternProperties: "^.*@[0-9a-f]+$": type: object @@ -63,9 +71,12 @@ patternProperties: examples: - | + #include <dt-bindings/gpio/gpio.h> + qfprom: eeprom@700000 { #address-cells = <1>; #size-cells = <1>; + wp-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; /* ... */ diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 0681d5fdd538..031eb64549af 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * at24.c - handle most I2C EEPROMs * @@ -6,23 +6,23 @@ * Copyright (C) 2008 Wolfram Sang, Pengutronix */ -#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/i2c.h> #include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/slab.h> -#include <linux/delay.h> #include <linux/mutex.h> -#include <linux/mod_devicetable.h> -#include <linux/bitops.h> -#include <linux/jiffies.h> -#include <linux/property.h> -#include <linux/acpi.h> -#include <linux/i2c.h> #include <linux/nvmem-provider.h> -#include <linux/regmap.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> -#include <linux/gpio/consumer.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> /* Address pointer is 16 bit. */ #define AT24_FLAG_ADDR16 BIT(7) @@ -88,8 +88,7 @@ struct at24_data { u8 flags; struct nvmem_device *nvmem; - - struct gpio_desc *wp_gpio; + struct regulator *vcc_reg; /* * Some chips tie up multiple I2C addresses; dummy devices reserve @@ -457,12 +456,10 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) * from this host, but not from other I2C masters. */ mutex_lock(&at24->lock); - gpiod_set_value_cansleep(at24->wp_gpio, 0); while (count) { ret = at24_regmap_write(at24, buf, off, count); if (ret < 0) { - gpiod_set_value_cansleep(at24->wp_gpio, 1); mutex_unlock(&at24->lock); pm_runtime_put(dev); return ret; @@ -472,7 +469,6 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) count -= ret; } - gpiod_set_value_cansleep(at24->wp_gpio, 1); mutex_unlock(&at24->lock); pm_runtime_put(dev); @@ -662,9 +658,9 @@ static int at24_probe(struct i2c_client *client) at24->client[0].client = client; at24->client[0].regmap = regmap; - at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH); - if (IS_ERR(at24->wp_gpio)) - return PTR_ERR(at24->wp_gpio); + at24->vcc_reg = devm_regulator_get(dev, "vcc"); + if (IS_ERR(at24->vcc_reg)) + return PTR_ERR(at24->vcc_reg); writable = !(flags & AT24_FLAG_READONLY); if (writable) { @@ -701,6 +697,12 @@ static int at24_probe(struct i2c_client *client) i2c_set_clientdata(client, at24); + err = regulator_enable(at24->vcc_reg); + if (err) { + dev_err(dev, "Failed to enable vcc regulator\n"); + return err; + } + /* enable runtime pm */ pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -713,6 +715,7 @@ static int at24_probe(struct i2c_client *client) pm_runtime_idle(dev); if (err) { pm_runtime_disable(dev); + regulator_disable(at24->vcc_reg); return -ENODEV; } @@ -728,15 +731,42 @@ static int at24_probe(struct i2c_client *client) static int at24_remove(struct i2c_client *client) { + struct at24_data *at24 = i2c_get_clientdata(client); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + regulator_disable(at24->vcc_reg); pm_runtime_set_suspended(&client->dev); return 0; } +static int __maybe_unused at24_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_data *at24 = i2c_get_clientdata(client); + + return regulator_disable(at24->vcc_reg); +} + +static int __maybe_unused at24_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_data *at24 = i2c_get_clientdata(client); + + return regulator_enable(at24->vcc_reg); +} + +static const struct dev_pm_ops at24_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL) +}; + static struct i2c_driver at24_driver = { .driver = { .name = "at24", + .pm = &at24_pm_ops, .of_match_table = at24_of_match, .acpi_match_table = ACPI_PTR(at24_acpi_ids), }, diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9f1ee9c766ec..408ce702347e 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> +#include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/slab.h> #include "nvmem.h" @@ -54,8 +55,14 @@ static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (nvmem->reg_write) - return nvmem->reg_write(nvmem->priv, offset, val, bytes); + int ret; + + if (nvmem->reg_write) { + gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); + gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + return ret; + } return -EINVAL; } @@ -338,6 +345,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) kfree(nvmem); return ERR_PTR(rval); } + if (config->wp_gpio) + nvmem->wp_gpio = config->wp_gpio; + else + nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp", + GPIOD_OUT_HIGH); + if (IS_ERR(nvmem->wp_gpio)) + return ERR_CAST(nvmem->wp_gpio); + kref_init(&nvmem->refcnt); INIT_LIST_HEAD(&nvmem->cells); diff --git a/drivers/nvmem/nvmem.h b/drivers/nvmem/nvmem.h index eb8ed7121fa3..be0d66d75c8a 100644 --- a/drivers/nvmem/nvmem.h +++ b/drivers/nvmem/nvmem.h @@ -9,6 +9,7 @@ #include <linux/list.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> +#include <linux/gpio/consumer.h> struct nvmem_device { struct module *owner; @@ -26,6 +27,7 @@ struct nvmem_device { struct list_head cells; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; + struct gpio_desc *wp_gpio; void *priv; }; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index fe051323be0a..6d6f8e5d24c9 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -11,6 +11,7 @@ #include <linux/err.h> #include <linux/errno.h> +#include <linux/gpio/consumer.h> struct nvmem_device; struct nvmem_cell_info; @@ -45,6 +46,7 @@ enum nvmem_type { * @word_size: Minimum read/write access granularity. * @stride: Minimum read/write access stride. * @priv: User context passed to read/write callbacks. + * @wp-gpio: Write protect pin * * Note: A default "nvmem<id>" name will be assigned to the device if * no name is specified in its configuration. In such case "<id>" is @@ -58,6 +60,7 @@ struct nvmem_config { const char *name; int id; struct module *owner; + struct gpio_desc *wp_gpio; const struct nvmem_cell_info *cells; int ncells; enum nvmem_type type; |