diff options
-rw-r--r-- | Documentation/devicetree/bindings/mfd/bd9571mwv.txt | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-bd9571mwv.c | 35 | ||||
-rw-r--r-- | drivers/mfd/arizona-core.c | 11 | ||||
-rw-r--r-- | drivers/mfd/arizona-i2c.c | 11 | ||||
-rw-r--r-- | drivers/mfd/arizona-spi.c | 138 | ||||
-rw-r--r-- | drivers/mfd/arizona.h | 9 | ||||
-rw-r--r-- | drivers/mfd/bd9571mwv.c | 178 | ||||
-rw-r--r-- | drivers/regulator/bd9571mwv-regulator.c | 59 | ||||
-rw-r--r-- | include/linux/mfd/bd9571mwv.h | 45 | ||||
-rw-r--r-- | include/linux/mfd/rohm-generic.h | 2 |
10 files changed, 327 insertions, 165 deletions
diff --git a/Documentation/devicetree/bindings/mfd/bd9571mwv.txt b/Documentation/devicetree/bindings/mfd/bd9571mwv.txt index 8c4678650d1a..1d6413e96c37 100644 --- a/Documentation/devicetree/bindings/mfd/bd9571mwv.txt +++ b/Documentation/devicetree/bindings/mfd/bd9571mwv.txt @@ -1,7 +1,7 @@ -* ROHM BD9571MWV Power Management Integrated Circuit (PMIC) bindings +* ROHM BD9571MWV/BD9574MWF Power Management Integrated Circuit (PMIC) bindings Required properties: - - compatible : Should be "rohm,bd9571mwv". + - compatible : Should be "rohm,bd9571mwv" or "rohm,bd9574mwf". - reg : I2C slave address. - interrupts : The interrupt line the device is connected to. - interrupt-controller : Marks the device node as an interrupt controller. diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index c0abc9c6851b..df6102b57734 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -1,31 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * ROHM BD9571MWV-M GPIO driver + * ROHM BD9571MWV-M and BD9574MWF-M GPIO driver * * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65086 driver * * NOTE: Interrupts are not supported yet. */ #include <linux/gpio/driver.h> +#include <linux/mfd/rohm-generic.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mfd/bd9571mwv.h> struct bd9571mwv_gpio { + struct regmap *regmap; struct gpio_chip chip; - struct bd9571mwv *bd; }; static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip, @@ -34,7 +27,7 @@ static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip, struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); int ret, val; - ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_DIR, &val); + ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_DIR, &val); if (ret < 0) return ret; if (val & BIT(offset)) @@ -48,8 +41,7 @@ static int bd9571mwv_gpio_direction_input(struct gpio_chip *chip, { struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR, - BIT(offset), 0); + regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR, BIT(offset), 0); return 0; } @@ -60,9 +52,9 @@ static int bd9571mwv_gpio_direction_output(struct gpio_chip *chip, struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); /* Set the initial value */ - regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT, + regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT, BIT(offset), value ? BIT(offset) : 0); - regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR, + regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR, BIT(offset), BIT(offset)); return 0; @@ -73,7 +65,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset) struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); int ret, val; - ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_IN, &val); + ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_IN, &val); if (ret < 0) return ret; @@ -85,7 +77,7 @@ static void bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset, { struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT, + regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT, BIT(offset), value ? BIT(offset) : 0); } @@ -113,9 +105,9 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); - gpio->bd = dev_get_drvdata(pdev->dev.parent); + gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL); gpio->chip = template_chip; - gpio->chip.parent = gpio->bd->dev; + gpio->chip.parent = pdev->dev.parent; ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); if (ret < 0) { @@ -127,7 +119,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_gpio_id_table[] = { - { "bd9571mwv-gpio", }, + { "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 }, + { "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table); diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 000cb82023e3..75f1bc671d59 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -797,17 +797,6 @@ const struct dev_pm_ops arizona_pm_ops = { EXPORT_SYMBOL_GPL(arizona_pm_ops); #ifdef CONFIG_OF -unsigned long arizona_of_get_type(struct device *dev) -{ - const struct of_device_id *id = of_match_device(arizona_of_match, dev); - - if (id) - return (unsigned long)id->data; - else - return 0; -} -EXPORT_SYMBOL_GPL(arizona_of_get_type); - static int arizona_of_get_core_pdata(struct arizona *arizona) { struct arizona_pdata *pdata = &arizona->pdata; diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 4b58e3ad6eb6..5e83b730c4ce 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -23,14 +23,16 @@ static int arizona_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + const void *match_data; struct arizona *arizona; const struct regmap_config *regmap_config = NULL; - unsigned long type; + unsigned long type = 0; int ret; - if (i2c->dev.of_node) - type = arizona_of_get_type(&i2c->dev); - else + match_data = device_get_match_data(&i2c->dev); + if (match_data) + type = (unsigned long)match_data; + else if (id) type = id->driver_data; switch (type) { @@ -115,6 +117,7 @@ static struct i2c_driver arizona_i2c_driver = { module_i2c_driver(arizona_i2c_driver); +MODULE_SOFTDEP("pre: arizona_ldo1"); MODULE_DESCRIPTION("Arizona I2C bus interface"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 2633e147b76c..24a2c75d691a 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -7,7 +7,10 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ +#include <linux/acpi.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -15,22 +18,141 @@ #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/of.h> +#include <uapi/linux/input-event-codes.h> #include <linux/mfd/arizona/core.h> #include "arizona.h" +#ifdef CONFIG_ACPI +const struct acpi_gpio_params reset_gpios = { 1, 0, false }; +const struct acpi_gpio_params ldoena_gpios = { 2, 0, false }; + +static const struct acpi_gpio_mapping arizona_acpi_gpios[] = { + { "reset-gpios", &reset_gpios, 1, }, + { "wlf,ldoena-gpios", &ldoena_gpios, 1 }, + { } +}; + +/* + * The ACPI resources for the device only describe external GPIO-s. They do + * not provide mappings for the GPIO-s coming from the Arizona codec itself. + */ +static const struct gpiod_lookup arizona_soc_gpios[] = { + { "arizona", 2, "wlf,spkvdd-ena", 0, GPIO_ACTIVE_HIGH }, + { "arizona", 4, "wlf,micd-pol", 0, GPIO_ACTIVE_LOW }, +}; + +/* + * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: + * Function A Play/Pause: 0 ohm + * Function D Voice assistant: 135 ohm + * Function B Volume Up 240 ohm + * Function C Volume Down 470 ohm + * Minimum Mic DC resistance 1000 ohm + * Minimum Ear speaker impedance 16 ohm + * Note the first max value below must be less then the min. speaker impedance, + * to allow CTIA/OMTP detection to work. The other max values are the closest + * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. + */ +static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { + { .max = 11, .key = KEY_PLAYPAUSE }, + { .max = 186, .key = KEY_VOICECOMMAND }, + { .max = 348, .key = KEY_VOLUMEUP }, + { .max = 752, .key = KEY_VOLUMEDOWN }, +}; + +static void arizona_spi_acpi_remove_lookup(void *lookup) +{ + gpiod_remove_lookup_table(lookup); +} + +static int arizona_spi_acpi_probe(struct arizona *arizona) +{ + struct gpiod_lookup_table *lookup; + acpi_status status; + int ret; + + /* Add mappings for the 2 ACPI declared GPIOs used for reset and ldo-ena */ + devm_acpi_dev_add_driver_gpios(arizona->dev, arizona_acpi_gpios); + + /* Add lookups for the SoCs own GPIOs used for micdet-polarity and spkVDD-enable */ + lookup = devm_kzalloc(arizona->dev, + struct_size(lookup, table, ARRAY_SIZE(arizona_soc_gpios) + 1), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + lookup->dev_id = dev_name(arizona->dev); + memcpy(lookup->table, arizona_soc_gpios, sizeof(arizona_soc_gpios)); + + gpiod_add_lookup_table(lookup); + ret = devm_add_action_or_reset(arizona->dev, arizona_spi_acpi_remove_lookup, lookup); + if (ret) + return ret; + + /* Enable 32KHz clock from SoC to codec for jack-detect */ + status = acpi_evaluate_object(ACPI_HANDLE(arizona->dev), "CLKE", NULL, NULL); + if (ACPI_FAILURE(status)) + dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n", status); + + /* + * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING + * The IRQ line will stay low when a new IRQ event happens between reading + * the IRQ status flags and acknowledging them. When the IRQ line stays + * low like this the IRQ will never trigger again when its type is set + * to IRQF_TRIGGER_FALLING. Correct the IRQ trigger-type to fix this. + * + * Note theoretically it is possible that some boards are not capable + * of handling active low level interrupts. In that case setting the + * flag to IRQF_TRIGGER_FALLING would not be a bug (and we would need + * to work around this) but so far all known usages of IRQF_TRIGGER_FALLING + * are a bug in the board's DSDT. + */ + arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; + + /* Wait 200 ms after jack insertion */ + arizona->pdata.micd_detect_debounce = 200; + + /* Use standard AOSP values for headset-button mappings */ + arizona->pdata.micd_ranges = arizona_micd_aosp_ranges; + arizona->pdata.num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges); + + return 0; +} + +static const struct acpi_device_id arizona_acpi_match[] = { + { + .id = "WM510204", + .driver_data = WM5102, + }, + { + .id = "WM510205", + .driver_data = WM5102, + }, + { } +}; +MODULE_DEVICE_TABLE(acpi, arizona_acpi_match); +#else +static int arizona_spi_acpi_probe(struct arizona *arizona) +{ + return -ENODEV; +} +#endif + static int arizona_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); + const void *match_data; struct arizona *arizona; const struct regmap_config *regmap_config = NULL; - unsigned long type; + unsigned long type = 0; int ret; - if (spi->dev.of_node) - type = arizona_of_get_type(&spi->dev); - else + match_data = device_get_match_data(&spi->dev); + if (match_data) + type = (unsigned long)match_data; + else if (id) type = id->driver_data; switch (type) { @@ -75,6 +197,12 @@ static int arizona_spi_probe(struct spi_device *spi) arizona->dev = &spi->dev; arizona->irq = spi->irq; + if (has_acpi_companion(&spi->dev)) { + ret = arizona_spi_acpi_probe(arizona); + if (ret) + return ret; + } + return arizona_dev_init(arizona); } @@ -102,6 +230,7 @@ static struct spi_driver arizona_spi_driver = { .name = "arizona", .pm = &arizona_pm_ops, .of_match_table = of_match_ptr(arizona_of_match), + .acpi_match_table = ACPI_PTR(arizona_acpi_match), }, .probe = arizona_spi_probe, .remove = arizona_spi_remove, @@ -110,6 +239,7 @@ static struct spi_driver arizona_spi_driver = { module_spi_driver(arizona_spi_driver); +MODULE_SOFTDEP("pre: arizona_ldo1"); MODULE_DESCRIPTION("Arizona SPI bus interface"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h index 995efc6d7f32..801cbbcd71cb 100644 --- a/drivers/mfd/arizona.h +++ b/drivers/mfd/arizona.h @@ -50,13 +50,4 @@ int arizona_dev_exit(struct arizona *arizona); int arizona_irq_init(struct arizona *arizona); int arizona_irq_exit(struct arizona *arizona); -#ifdef CONFIG_OF -unsigned long arizona_of_get_type(struct device *dev); -#else -static inline unsigned long arizona_of_get_type(struct device *dev) -{ - return 0; -} -#endif - #endif diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index fab3cdc27ed6..e15b1acfb063 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -1,16 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * ROHM BD9571MWV-M MFD driver + * ROHM BD9571MWV-M and BD9574MVF-M core driver * * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. + * Copyright (C) 2020 Renesas Electronics Corporation * * Based on the TPS65086 driver */ @@ -18,6 +11,7 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/mfd/core.h> +#include <linux/mfd/rohm-generic.h> #include <linux/module.h> #include <linux/mfd/bd9571mwv.h> @@ -110,13 +104,78 @@ static struct regmap_irq_chip bd9571mwv_irq_chip = { .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), }; -static int bd9571mwv_identify(struct bd9571mwv *bd) +static const struct mfd_cell bd9574mwf_cells[] = { + { .name = "bd9574mwf-regulator", }, + { .name = "bd9574mwf-gpio", }, +}; + +static const struct regmap_range bd9574mwf_readable_yes_ranges[] = { + regmap_reg_range(BD9571MWV_VENDOR_CODE, BD9571MWV_PRODUCT_REVISION), + regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), + regmap_reg_range(BD9571MWV_DVFS_VINIT, BD9571MWV_DVFS_SETVMAX), + regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_MONIVDAC), + regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), + regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INTMASK), + regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), +}; + +static const struct regmap_access_table bd9574mwf_readable_table = { + .yes_ranges = bd9574mwf_readable_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(bd9574mwf_readable_yes_ranges), +}; + +static const struct regmap_range bd9574mwf_writable_yes_ranges[] = { + regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), + regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_SETVID), + regmap_reg_range(BD9571MWV_GPIO_DIR, BD9571MWV_GPIO_OUT), + regmap_reg_range(BD9571MWV_GPIO_INT_SET, BD9571MWV_GPIO_INTMASK), + regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), +}; + +static const struct regmap_access_table bd9574mwf_writable_table = { + .yes_ranges = bd9574mwf_writable_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(bd9574mwf_writable_yes_ranges), +}; + +static const struct regmap_range bd9574mwf_volatile_yes_ranges[] = { + regmap_reg_range(BD9571MWV_DVFS_MONIVDAC, BD9571MWV_DVFS_MONIVDAC), + regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), + regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INT), + regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTREQ), +}; + +static const struct regmap_access_table bd9574mwf_volatile_table = { + .yes_ranges = bd9574mwf_volatile_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(bd9574mwf_volatile_yes_ranges), +}; + +static const struct regmap_config bd9574mwf_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .rd_table = &bd9574mwf_readable_table, + .wr_table = &bd9574mwf_writable_table, + .volatile_table = &bd9574mwf_volatile_table, + .max_register = 0xff, +}; + +static struct regmap_irq_chip bd9574mwf_irq_chip = { + .name = "bd9574mwf", + .status_base = BD9571MWV_INT_INTREQ, + .mask_base = BD9571MWV_INT_INTMASK, + .ack_base = BD9571MWV_INT_INTREQ, + .init_ack_masked = true, + .num_regs = 1, + .irqs = bd9571mwv_irqs, + .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), +}; + +static int bd957x_identify(struct device *dev, struct regmap *regmap) { - struct device *dev = bd->dev; unsigned int value; int ret; - ret = regmap_read(bd->regmap, BD9571MWV_VENDOR_CODE, &value); + ret = regmap_read(regmap, BD9571MWV_VENDOR_CODE, &value); if (ret) { dev_err(dev, "Failed to read vendor code register (ret=%i)\n", ret); @@ -129,84 +188,82 @@ static int bd9571mwv_identify(struct bd9571mwv *bd) return -EINVAL; } - ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_CODE, &value); + ret = regmap_read(regmap, BD9571MWV_PRODUCT_CODE, &value); if (ret) { dev_err(dev, "Failed to read product code register (ret=%i)\n", ret); return ret; } - - if (value != BD9571MWV_PRODUCT_CODE_VAL) { - dev_err(dev, "Invalid product code ID %02x (expected %02x)\n", - value, BD9571MWV_PRODUCT_CODE_VAL); - return -EINVAL; - } - - ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_REVISION, &value); + ret = regmap_read(regmap, BD9571MWV_PRODUCT_REVISION, &value); if (ret) { dev_err(dev, "Failed to read revision register (ret=%i)\n", ret); return ret; } - dev_info(dev, "Device: BD9571MWV rev. %d\n", value & 0xff); - return 0; } static int bd9571mwv_probe(struct i2c_client *client, - const struct i2c_device_id *ids) + const struct i2c_device_id *ids) { - struct bd9571mwv *bd; - int ret; - - bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL); - if (!bd) - return -ENOMEM; + const struct regmap_config *regmap_config; + const struct regmap_irq_chip *irq_chip; + const struct mfd_cell *cells; + struct device *dev = &client->dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + int ret, num_cells, irq = client->irq; + + /* Read the PMIC product code */ + ret = i2c_smbus_read_byte_data(client, BD9571MWV_PRODUCT_CODE); + if (ret < 0) { + dev_err(dev, "Failed to read product code\n"); + return ret; + } - i2c_set_clientdata(client, bd); - bd->dev = &client->dev; - bd->irq = client->irq; + switch (ret) { + case BD9571MWV_PRODUCT_CODE_BD9571MWV: + regmap_config = &bd9571mwv_regmap_config; + irq_chip = &bd9571mwv_irq_chip; + cells = bd9571mwv_cells; + num_cells = ARRAY_SIZE(bd9571mwv_cells); + break; + case BD9571MWV_PRODUCT_CODE_BD9574MWF: + regmap_config = &bd9574mwf_regmap_config; + irq_chip = &bd9574mwf_irq_chip; + cells = bd9574mwf_cells; + num_cells = ARRAY_SIZE(bd9574mwf_cells); + break; + default: + dev_err(dev, "Unsupported device 0x%x\n", ret); + return -ENODEV; + } - bd->regmap = devm_regmap_init_i2c(client, &bd9571mwv_regmap_config); - if (IS_ERR(bd->regmap)) { - dev_err(bd->dev, "Failed to initialize register map\n"); - return PTR_ERR(bd->regmap); + regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to initialize register map\n"); + return PTR_ERR(regmap); } - ret = bd9571mwv_identify(bd); + ret = bd957x_identify(dev, regmap); if (ret) return ret; - ret = regmap_add_irq_chip(bd->regmap, bd->irq, IRQF_ONESHOT, 0, - &bd9571mwv_irq_chip, &bd->irq_data); - if (ret) { - dev_err(bd->dev, "Failed to register IRQ chip\n"); - return ret; - } - - ret = mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, bd9571mwv_cells, - ARRAY_SIZE(bd9571mwv_cells), NULL, 0, - regmap_irq_get_domain(bd->irq_data)); + ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, + irq_chip, &irq_data); if (ret) { - regmap_del_irq_chip(bd->irq, bd->irq_data); + dev_err(dev, "Failed to register IRQ chip\n"); return ret; } - return 0; -} - -static int bd9571mwv_remove(struct i2c_client *client) -{ - struct bd9571mwv *bd = i2c_get_clientdata(client); - - regmap_del_irq_chip(bd->irq, bd->irq_data); - - return 0; + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, num_cells, + NULL, 0, regmap_irq_get_domain(irq_data)); } static const struct of_device_id bd9571mwv_of_match_table[] = { { .compatible = "rohm,bd9571mwv", }, + { .compatible = "rohm,bd9574mwf", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table); @@ -223,7 +280,6 @@ static struct i2c_driver bd9571mwv_driver = { .of_match_table = bd9571mwv_of_match_table, }, .probe = bd9571mwv_probe, - .remove = bd9571mwv_remove, .id_table = bd9571mwv_id_table, }; module_i2c_driver(bd9571mwv_driver); diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index e690c2ce5b3c..7b0cd08db446 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * ROHM BD9571MWV-M regulator driver + * ROHM BD9571MWV-M and BD9574MWF-M regulator driver * * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> * @@ -9,6 +9,7 @@ * NOTE: VD09 is missing */ +#include <linux/mfd/rohm-generic.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -17,7 +18,7 @@ #include <linux/mfd/bd9571mwv.h> struct bd9571mwv_reg { - struct bd9571mwv *bd; + struct regmap *regmap; /* DDR Backup Power */ u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ @@ -137,26 +138,30 @@ static const struct regulator_desc regulators[] = { }; #ifdef CONFIG_PM_SLEEP -static int bd9571mwv_bkup_mode_read(struct bd9571mwv *bd, unsigned int *mode) +static int bd9571mwv_bkup_mode_read(struct bd9571mwv_reg *bdreg, + unsigned int *mode) { int ret; - ret = regmap_read(bd->regmap, BD9571MWV_BKUP_MODE_CNT, mode); + ret = regmap_read(bdreg->regmap, BD9571MWV_BKUP_MODE_CNT, mode); if (ret) { - dev_err(bd->dev, "failed to read backup mode (%d)\n", ret); + dev_err(regmap_get_device(bdreg->regmap), + "failed to read backup mode (%d)\n", ret); return ret; } return 0; } -static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode) +static int bd9571mwv_bkup_mode_write(struct bd9571mwv_reg *bdreg, + unsigned int mode) { int ret; - ret = regmap_write(bd->regmap, BD9571MWV_BKUP_MODE_CNT, mode); + ret = regmap_write(bdreg->regmap, BD9571MWV_BKUP_MODE_CNT, mode); if (ret) { - dev_err(bd->dev, "failed to configure backup mode 0x%x (%d)\n", + dev_err(regmap_get_device(bdreg->regmap), + "failed to configure backup mode 0x%x (%d)\n", mode, ret); return ret; } @@ -194,7 +199,7 @@ static ssize_t backup_mode_store(struct device *dev, * Configure DDR Backup Mode, to change the role of the accessory power * switch from a power switch to a wake-up switch, or vice versa */ - ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode); + ret = bd9571mwv_bkup_mode_read(bdreg, &mode); if (ret) return ret; @@ -202,7 +207,7 @@ static ssize_t backup_mode_store(struct device *dev, if (bdreg->bkup_mode_enabled) mode |= bdreg->bkup_mode_cnt_keepon; - ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode); + ret = bd9571mwv_bkup_mode_write(bdreg, mode); if (ret) return ret; @@ -221,7 +226,7 @@ static int bd9571mwv_suspend(struct device *dev) return 0; /* Save DDR Backup Mode */ - ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode); + ret = bd9571mwv_bkup_mode_read(bdreg, &mode); if (ret) return ret; @@ -235,7 +240,7 @@ static int bd9571mwv_suspend(struct device *dev) mode |= bdreg->bkup_mode_cnt_keepon; if (mode != bdreg->bkup_mode_cnt_saved) - return bd9571mwv_bkup_mode_write(bdreg->bd, mode); + return bd9571mwv_bkup_mode_write(bdreg, mode); return 0; } @@ -248,7 +253,7 @@ static int bd9571mwv_resume(struct device *dev) return 0; /* Restore DDR Backup Mode */ - return bd9571mwv_bkup_mode_write(bdreg->bd, bdreg->bkup_mode_cnt_saved); + return bd9571mwv_bkup_mode_write(bdreg, bdreg->bkup_mode_cnt_saved); } static const struct dev_pm_ops bd9571mwv_pm = { @@ -268,51 +273,54 @@ static int bd9571mwv_regulator_remove(struct platform_device *pdev) static int bd9571mwv_regulator_probe(struct platform_device *pdev) { - struct bd9571mwv *bd = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = { }; struct bd9571mwv_reg *bdreg; struct regulator_dev *rdev; unsigned int val; int i; + enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; bdreg = devm_kzalloc(&pdev->dev, sizeof(*bdreg), GFP_KERNEL); if (!bdreg) return -ENOMEM; - bdreg->bd = bd; + bdreg->regmap = dev_get_regmap(pdev->dev.parent, NULL); platform_set_drvdata(pdev, bdreg); config.dev = &pdev->dev; - config.dev->of_node = bd->dev->of_node; - config.driver_data = bd; - config.regmap = bd->regmap; + config.dev->of_node = pdev->dev.parent->of_node; + config.driver_data = bdreg; + config.regmap = bdreg->regmap; for (i = 0; i < ARRAY_SIZE(regulators); i++) { + /* BD9574MWF supports DVFS only */ + if (chip == ROHM_CHIP_TYPE_BD9574 && regulators[i].id != DVFS) + continue; rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(rdev)) { - dev_err(bd->dev, "failed to register %s regulator\n", + dev_err(&pdev->dev, "failed to register %s regulator\n", pdev->name); return PTR_ERR(rdev); } } val = 0; - of_property_read_u32(bd->dev->of_node, "rohm,ddr-backup-power", &val); + of_property_read_u32(config.dev->of_node, "rohm,ddr-backup-power", &val); if (val & ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK) { - dev_err(bd->dev, "invalid %s mode %u\n", + dev_err(&pdev->dev, "invalid %s mode %u\n", "rohm,ddr-backup-power", val); return -EINVAL; } bdreg->bkup_mode_cnt_keepon = val; - bdreg->rstbmode_level = of_property_read_bool(bd->dev->of_node, + bdreg->rstbmode_level = of_property_read_bool(config.dev->of_node, "rohm,rstbmode-level"); - bdreg->rstbmode_pulse = of_property_read_bool(bd->dev->of_node, + bdreg->rstbmode_pulse = of_property_read_bool(config.dev->of_node, "rohm,rstbmode-pulse"); if (bdreg->rstbmode_level && bdreg->rstbmode_pulse) { - dev_err(bd->dev, "only one rohm,rstbmode-* may be specified"); + dev_err(&pdev->dev, "only one rohm,rstbmode-* may be specified"); return -EINVAL; } @@ -336,7 +344,8 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_regulator_id_table[] = { - { "bd9571mwv-regulator", }, + { "bd9571mwv-regulator", ROHM_CHIP_TYPE_BD9571 }, + { "bd9574mwf-regulator", ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table); diff --git a/include/linux/mfd/bd9571mwv.h b/include/linux/mfd/bd9571mwv.h index eb05569f752b..8efd99d07c9e 100644 --- a/include/linux/mfd/bd9571mwv.h +++ b/include/linux/mfd/bd9571mwv.h @@ -1,16 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * ROHM BD9571MWV-M driver + * ROHM BD9571MWV-M and BD9574MWF-M driver * * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. + * Copyright (C) 2020 Renesas Electronics Corporation * * Based on the TPS65086 driver */ @@ -21,11 +14,12 @@ #include <linux/device.h> #include <linux/regmap.h> -/* List of registers for BD9571MWV */ +/* List of registers for BD9571MWV and BD9574MWF */ #define BD9571MWV_VENDOR_CODE 0x00 #define BD9571MWV_VENDOR_CODE_VAL 0xdb #define BD9571MWV_PRODUCT_CODE 0x01 -#define BD9571MWV_PRODUCT_CODE_VAL 0x60 +#define BD9571MWV_PRODUCT_CODE_BD9571MWV 0x60 +#define BD9571MWV_PRODUCT_CODE_BD9574MWF 0x74 #define BD9571MWV_PRODUCT_REVISION 0x02 #define BD9571MWV_I2C_FUSA_MODE 0x10 @@ -55,6 +49,7 @@ #define BD9571MWV_VD33_VID 0x44 #define BD9571MWV_DVFS_VINIT 0x50 +#define BD9574MWF_VD09_VINIT 0x51 #define BD9571MWV_DVFS_SETVMAX 0x52 #define BD9571MWV_DVFS_BOOSTVID 0x53 #define BD9571MWV_DVFS_SETVID 0x54 @@ -68,6 +63,7 @@ #define BD9571MWV_GPIO_INT_SET 0x64 #define BD9571MWV_GPIO_INT 0x65 #define BD9571MWV_GPIO_INTMASK 0x66 +#define BD9574MWF_GPIO_MUX 0x67 #define BD9571MWV_REG_KEEP(n) (0x70 + (n)) @@ -77,6 +73,8 @@ #define BD9571MWV_PROT_ERROR_STATUS2 0x83 #define BD9571MWV_PROT_ERROR_STATUS3 0x84 #define BD9571MWV_PROT_ERROR_STATUS4 0x85 +#define BD9574MWF_PROT_ERROR_STATUS5 0x86 +#define BD9574MWF_SYSTEM_ERROR_STATUS 0x87 #define BD9571MWV_INT_INTREQ 0x90 #define BD9571MWV_INT_INTREQ_MD1_INT BIT(0) @@ -89,6 +87,12 @@ #define BD9571MWV_INT_INTREQ_BKUP_TRG_INT BIT(7) #define BD9571MWV_INT_INTMASK 0x91 +#define BD9574MWF_SSCG_CNT 0xA0 +#define BD9574MWF_POFFB_MRB 0xA1 +#define BD9574MWF_SMRB_WR_PROT 0xA2 +#define BD9574MWF_SMRB_ASSERT 0xA3 +#define BD9574MWF_SMRB_STATUS 0xA4 + #define BD9571MWV_ACCESS_KEY 0xff /* Define the BD9571MWV IRQ numbers */ @@ -98,23 +102,8 @@ enum bd9571mwv_irqs { BD9571MWV_IRQ_MD2_E2, BD9571MWV_IRQ_PROT_ERR, BD9571MWV_IRQ_GP, - BD9571MWV_IRQ_128H_OF, + BD9571MWV_IRQ_128H_OF, /* BKUP_HOLD on BD9574MWF */ BD9571MWV_IRQ_WDT_OF, BD9571MWV_IRQ_BKUP_TRG, }; - -/** - * struct bd9571mwv - state holder for the bd9571mwv driver - * - * Device data may be used to access the BD9571MWV chip - */ -struct bd9571mwv { - struct device *dev; - struct regmap *regmap; - - /* IRQ Data */ - int irq; - struct regmap_irq_chip_data *irq_data; -}; - #endif /* __LINUX_MFD_BD9571MWV_H */ diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h index 4283b5b33e04..affacf8c6e91 100644 --- a/include/linux/mfd/rohm-generic.h +++ b/include/linux/mfd/rohm-generic.h @@ -12,6 +12,8 @@ enum rohm_chip_type { ROHM_CHIP_TYPE_BD71847, ROHM_CHIP_TYPE_BD70528, ROHM_CHIP_TYPE_BD71828, + ROHM_CHIP_TYPE_BD9571, + ROHM_CHIP_TYPE_BD9574, ROHM_CHIP_TYPE_AMOUNT }; |