From c58ad0f2b052b5675d6394e03713ee41e721b44c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:00:56 +0900 Subject: [PATCH 01/32] mfd: bd9571mwv: Use devm_mfd_add_devices() To remove mfd devices when unload this driver, should use devm_mfd_add_devices() instead. Fixes: d3ea21272094 ("mfd: Add ROHM BD9571MWV-M MFD PMIC driver") Signed-off-by: Yoshihiro Shimoda Acked-for-MFD-by: Lee Jones Reviewed-by: Geert Uytterhoeven Reviewed-by: Matti Vaittinen Signed-off-by: Lee Jones --- drivers/mfd/bd9571mwv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index fab3cdc27ed6..19d57a45134c 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -185,9 +185,9 @@ static int bd9571mwv_probe(struct i2c_client *client, 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_mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, + bd9571mwv_cells, ARRAY_SIZE(bd9571mwv_cells), + NULL, 0, regmap_irq_get_domain(bd->irq_data)); if (ret) { regmap_del_irq_chip(bd->irq, bd->irq_data); return ret; From e413c27e2ec8276ac0f25e7d1203f29adcfd8758 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:00:57 +0900 Subject: [PATCH 02/32] dt-bindings: mfd: bd9571mwv: Document BD9574MWF Document other similar specification chip BD9574MWF. Signed-off-by: Yoshihiro Shimoda Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/bd9571mwv.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 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. From b0f87e8ac263e3b82ec314542cb7fb07a47fc8b7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:00:58 +0900 Subject: [PATCH 03/32] mfd: rohm-generic: Add BD9571 and BD9574 Add chip IDs for BD9571MWV and BD9574MWF. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- include/linux/mfd/rohm-generic.h | 2 ++ 1 file changed, 2 insertions(+) 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 }; From 30402f97f965fda9440e480f1aba6a6aba4572cd Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:00:59 +0900 Subject: [PATCH 04/32] regulator: bd9571mwv: rid of using struct bd9571mwv To simplify this driver, use dev_get_regmap() and rid of using struct bd9571mwv. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/bd9571mwv-regulator.c | 49 +++++++++++++------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index e690c2ce5b3c..42b6a7019c90 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -17,7 +17,7 @@ #include struct bd9571mwv_reg { - struct bd9571mwv *bd; + struct regmap *regmap; /* DDR Backup Power */ u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ @@ -137,26 +137,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 +198,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 +206,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 +225,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 +239,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 +252,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,7 +272,6 @@ 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; @@ -279,40 +282,40 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) 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++) { 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; } From ceee9dc13bf991b151701e3b720487b5e1589ba4 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:00 +0900 Subject: [PATCH 05/32] regulator: bd9571mwv: Add BD9574MWF support Add support for BD9574MWF which is similar chip with BD9571MWV. Note that we don't support voltage rails VD{09,18,25,33} by this driver on BD9574. The VD09 voltage could be read from PMIC but that is not supported by this commit. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/bd9571mwv-regulator.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index 42b6a7019c90..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 * @@ -9,6 +9,7 @@ * NOTE: VD09 is missing */ +#include #include #include #include @@ -277,6 +278,7 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) 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) @@ -292,6 +294,9 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) 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)) { @@ -339,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); From b9f71d14e570199bfd9a440db000e59780fe0fc7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:01 +0900 Subject: [PATCH 06/32] gpio: bd9571mwv: Use the SPDX license identifier Use the SPDX license identifier instead of a local description. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Reviewed-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Lee Jones --- drivers/gpio/gpio-bd9571mwv.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index c0abc9c6851b..abb622c28287 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ROHM BD9571MWV-M GPIO driver * * Copyright (C) 2017 Marek Vasut * - * 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. From 2d7af444e8364965a1ba44b2c2ea1e1122b673d4 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:02 +0900 Subject: [PATCH 07/32] gpio: bd9571mwv: rid of using struct bd9571mwv To simplify this driver, use dev_get_regmap() and rid of using struct bd9571mwv. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Lee Jones --- drivers/gpio/gpio-bd9571mwv.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index abb622c28287..0e5395ff7df5 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -16,8 +16,8 @@ #include struct bd9571mwv_gpio { + struct regmap *regmap; struct gpio_chip chip; - struct bd9571mwv *bd; }; static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip, @@ -26,7 +26,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)) @@ -40,8 +40,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; } @@ -52,9 +51,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; @@ -65,7 +64,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; @@ -77,7 +76,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); } @@ -105,9 +104,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) { From 2e35627e6956e743a7e8e8d17a86dd243a6d51ef Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:03 +0900 Subject: [PATCH 08/32] gpio: bd9571mwv: Add BD9574MWF support Add support for BD9574MWF which is similar chip with BD9571MWV. Note that BD9574MWF has additional features "RECOV_GPOUT", "FREQSEL" and "RTC_IN", but supports GPIO function only. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Lee Jones --- drivers/gpio/gpio-bd9571mwv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 0e5395ff7df5..df6102b57734 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -1,6 +1,6 @@ // 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 * @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -118,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); From bfb26be7fe90186e5d9fe704cc124ab77bb7d127 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:04 +0900 Subject: [PATCH 09/32] mfd: bd9571mwv: Use the SPDX license identifier Use the SPDX license identifier instead of a local description. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- drivers/mfd/bd9571mwv.c | 10 +--------- include/linux/mfd/bd9571mwv.h | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index 19d57a45134c..e68c3fabf9fc 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ROHM BD9571MWV-M MFD driver * * Copyright (C) 2017 Marek Vasut * - * 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 */ diff --git a/include/linux/mfd/bd9571mwv.h b/include/linux/mfd/bd9571mwv.h index eb05569f752b..bcc709293b24 100644 --- a/include/linux/mfd/bd9571mwv.h +++ b/include/linux/mfd/bd9571mwv.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ROHM BD9571MWV-M driver * * Copyright (C) 2017 Marek Vasut * - * 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 */ From 1e40a92c651f4bb383df757b69821f74820b6e6a Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Jan 2021 18:01:05 +0900 Subject: [PATCH 10/32] mfd: bd9571mwv: Use devm_regmap_add_irq_chip() Use devm_regmap_add_irq_chip() to simplify the code. Signed-off-by: Yoshihiro Shimoda Acked-for-MFD-by: Lee Jones Reviewed-by: Matti Vaittinen Reviewed-by: Geert Uytterhoeven Signed-off-by: Lee Jones --- drivers/mfd/bd9571mwv.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index e68c3fabf9fc..49e968e20ddc 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -170,31 +170,17 @@ static int bd9571mwv_probe(struct i2c_client *client, if (ret) return ret; - ret = regmap_add_irq_chip(bd->regmap, bd->irq, IRQF_ONESHOT, 0, - &bd9571mwv_irq_chip, &bd->irq_data); + ret = devm_regmap_add_irq_chip(bd->dev, 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 = devm_mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, - bd9571mwv_cells, ARRAY_SIZE(bd9571mwv_cells), - NULL, 0, regmap_irq_get_domain(bd->irq_data)); - if (ret) { - regmap_del_irq_chip(bd->irq, bd->irq_data); - 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(bd->dev, PLATFORM_DEVID_AUTO, + bd9571mwv_cells, ARRAY_SIZE(bd9571mwv_cells), + NULL, 0, regmap_irq_get_domain(bd->irq_data)); } static const struct of_device_id bd9571mwv_of_match_table[] = { @@ -215,7 +201,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); From f16e1fd197f85a943b5880009f4aefe05a17df0d Mon Sep 17 00:00:00 2001 From: Khiem Nguyen Date: Tue, 12 Jan 2021 18:01:06 +0900 Subject: [PATCH 11/32] mfd: bd9571mwv: Make the driver more generic Since the driver supports BD9571MWV PMIC only, this patch makes the functions and data structure become more generic so that it can support other PMIC variants as well. Also remove printing part name which Lee Jones suggested. Signed-off-by: Khiem Nguyen Co-developed-by: Yoshihiro Shimoda Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- drivers/mfd/bd9571mwv.c | 79 +++++++++++++++++++---------------- include/linux/mfd/bd9571mwv.h | 18 +------- 2 files changed, 44 insertions(+), 53 deletions(-) diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index 49e968e20ddc..2c1fcbb6ff2b 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -3,6 +3,7 @@ * ROHM BD9571MWV-M MFD driver * * Copyright (C) 2017 Marek Vasut + * Copyright (C) 2020 Renesas Electronics Corporation * * Based on the TPS65086 driver */ @@ -102,13 +103,12 @@ static struct regmap_irq_chip bd9571mwv_irq_chip = { .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), }; -static int bd9571mwv_identify(struct bd9571mwv *bd) +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); @@ -121,66 +121,71 @@ 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; + 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; - bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL); - if (!bd) - return -ENOMEM; - - i2c_set_clientdata(client, bd); - bd->dev = &client->dev; - bd->irq = client->irq; - - 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); + /* 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; } - ret = bd9571mwv_identify(bd); + 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; + default: + dev_err(dev, "Unsupported device 0x%x\n", ret); + return -ENODEV; + } + + 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 = bd957x_identify(dev, regmap); if (ret) return ret; - ret = devm_regmap_add_irq_chip(bd->dev, bd->regmap, bd->irq, - IRQF_ONESHOT, 0, &bd9571mwv_irq_chip, - &bd->irq_data); + ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, + irq_chip, &irq_data); if (ret) { - dev_err(bd->dev, "Failed to register IRQ chip\n"); + dev_err(dev, "Failed to register IRQ chip\n"); return ret; } - return devm_mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, - bd9571mwv_cells, ARRAY_SIZE(bd9571mwv_cells), - NULL, 0, regmap_irq_get_domain(bd->irq_data)); + 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[] = { diff --git a/include/linux/mfd/bd9571mwv.h b/include/linux/mfd/bd9571mwv.h index bcc709293b24..e1716eca6bb5 100644 --- a/include/linux/mfd/bd9571mwv.h +++ b/include/linux/mfd/bd9571mwv.h @@ -3,6 +3,7 @@ * ROHM BD9571MWV-M driver * * Copyright (C) 2017 Marek Vasut + * Copyright (C) 2020 Renesas Electronics Corporation * * Based on the TPS65086 driver */ @@ -17,7 +18,7 @@ #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_REVISION 0x02 #define BD9571MWV_I2C_FUSA_MODE 0x10 @@ -94,19 +95,4 @@ enum bd9571mwv_irqs { 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 */ From b2548da647bb04737196ffd945505d47a166239b Mon Sep 17 00:00:00 2001 From: Khiem Nguyen Date: Tue, 12 Jan 2021 18:01:07 +0900 Subject: [PATCH 12/32] mfd: bd9571mwv: Add support for BD9574MWF The new PMIC BD9574MWF inherits features from BD9571MWV. Add the support of new PMIC to existing bd9571mwv driver. Signed-off-by: Khiem Nguyen Co-developed-by: Yoshihiro Shimoda Signed-off-by: Yoshihiro Shimoda Reviewed-by: Matti Vaittinen Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- drivers/mfd/bd9571mwv.c | 76 ++++++++++++++++++++++++++++++++++- include/linux/mfd/bd9571mwv.h | 17 ++++++-- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c index 2c1fcbb6ff2b..e15b1acfb063 100644 --- a/drivers/mfd/bd9571mwv.c +++ b/drivers/mfd/bd9571mwv.c @@ -1,6 +1,6 @@ // 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 * Copyright (C) 2020 Renesas Electronics Corporation @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -103,6 +104,72 @@ static struct regmap_irq_chip bd9571mwv_irq_chip = { .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), }; +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) { unsigned int value; @@ -162,6 +229,12 @@ static int bd9571mwv_probe(struct i2c_client *client, 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; @@ -190,6 +263,7 @@ static int bd9571mwv_probe(struct i2c_client *client, 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); diff --git a/include/linux/mfd/bd9571mwv.h b/include/linux/mfd/bd9571mwv.h index e1716eca6bb5..8efd99d07c9e 100644 --- a/include/linux/mfd/bd9571mwv.h +++ b/include/linux/mfd/bd9571mwv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * ROHM BD9571MWV-M driver + * ROHM BD9571MWV-M and BD9574MWF-M driver * * Copyright (C) 2017 Marek Vasut * Copyright (C) 2020 Renesas Electronics Corporation @@ -14,11 +14,12 @@ #include #include -/* 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_BD9571MWV 0x60 +#define BD9571MWV_PRODUCT_CODE_BD9574MWF 0x74 #define BD9571MWV_PRODUCT_REVISION 0x02 #define BD9571MWV_I2C_FUSA_MODE 0x10 @@ -48,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 @@ -61,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)) @@ -70,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) @@ -82,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 */ @@ -91,7 +102,7 @@ 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, }; From b0b5b16b78cea1b2b990a69ab8e07a42ccf7a2ed Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 Dec 2020 23:52:48 +0100 Subject: [PATCH 13/32] mfd: altera-sysmgr: Fix physical address storing more A recent fix improved the way the resource gets passed to the low-level accessors, but left one warning that appears in configurations with a resource_size_t that is wider than a pointer: In file included from drivers/mfd/altera-sysmgr.c:19: drivers/mfd/altera-sysmgr.c: In function 'sysmgr_probe': drivers/mfd/altera-sysmgr.c:148:40: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] 148 | regmap = devm_regmap_init(dev, NULL, (void *)res->start, | ^ include/linux/regmap.h:646:6: note: in definition of macro '__regmap_lockdep_wrapper' 646 | fn(__VA_ARGS__, &_key, \ | ^~~~~~~~~~~ drivers/mfd/altera-sysmgr.c:148:12: note: in expansion of macro 'devm_regmap_init' 148 | regmap = devm_regmap_init(dev, NULL, (void *)res->start, | ^~~~~~~~~~~~~~~~ I had tried a different approach that would store the address in the private data as a phys_addr_t, but the easiest solution now seems to be to add a double cast to shut up the warning. As the address is passed to an inline assembly, it is guaranteed to not be wider than a register anyway. Fixes: d9ca7801b6e5 ("mfd: altera-sysmgr: Fix physical address storing hacks") Signed-off-by: Arnd Bergmann Signed-off-by: Lee Jones --- drivers/mfd/altera-sysmgr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c index 193a96c8b1ea..20cb294c7512 100644 --- a/drivers/mfd/altera-sysmgr.c +++ b/drivers/mfd/altera-sysmgr.c @@ -145,7 +145,8 @@ static int sysmgr_probe(struct platform_device *pdev) sysmgr_config.reg_write = s10_protected_reg_write; /* Need physical address for SMCC call */ - regmap = devm_regmap_init(dev, NULL, (void *)res->start, + regmap = devm_regmap_init(dev, NULL, + (void *)(uintptr_t)res->start, &sysmgr_config); } else { base = devm_ioremap(dev, res->start, resource_size(res)); From cd2b909bfb91e45f3faac08766230d9fad12cc28 Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Mon, 7 Dec 2020 15:14:46 +0100 Subject: [PATCH 14/32] mfd: sgi-ioc3: Turn Kconfig option into a bool Module builds of ioc3 fail with following errors: ERROR: "spurious_interrupt" [drivers/mfd/ioc3.ko] undefined! ERROR: "pci_find_host_bridge" [drivers/mfd/ioc3.ko] undefined! Exporting pci_find_host_bridge got rejected by maintainer, so easiest fix is to disable module building, which even makes sense since both SGI Origin and Octane always contain at least one IOC3 chip. Reported-by: kernel test robot Signed-off-by: Thomas Bogendoerfer Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index bdfce7b15621..79274fc49f36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2129,7 +2129,7 @@ config RAVE_SP_CORE device found on several devices in RAVE line of hardware. config SGI_MFD_IOC3 - tristate "SGI IOC3 core driver" + bool "SGI IOC3 core driver" depends on PCI && MIPS && 64BIT select MFD_CORE help From 951e92d141ea4ac5b68d9e6eac895e5988852d3c Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Fri, 4 Dec 2020 17:38:13 +0800 Subject: [PATCH 15/32] dt-bindings: mfd: Correct the node name of the panel LED According to the definition in leds-pwm.yaml, the node name of each LED must match the regular expression "^led(-[0-9a-f]+)?$". "led" or "led-" followed by a decimal or hexadecimal ID number. Signed-off-by: Zhen Lei Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/iqs62x.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/iqs62x.yaml b/Documentation/devicetree/bindings/mfd/iqs62x.yaml index 541b06d80e73..044cd7542c2b 100644 --- a/Documentation/devicetree/bindings/mfd/iqs62x.yaml +++ b/Documentation/devicetree/bindings/mfd/iqs62x.yaml @@ -93,7 +93,7 @@ examples: pwmleds { compatible = "pwm-leds"; - panel { + led-1 { pwms = <&iqs620a_pwm 0 1000000>; max-brightness = <255>; }; From 9c03008da125c1007919a9186628af3cc105f526 Mon Sep 17 00:00:00 2001 From: Timon Baetz Date: Tue, 22 Dec 2020 07:31:32 +0000 Subject: [PATCH 16/32] mfd: max8997: Add of_compatible to Extcon and Charger mfd_cell Add of_compatible ("maxim,max8997-muic") to the mfd_cell to have a of_node set in the extcon driver. Add of_compatible ("maxim,max8997-battery") to the mfd_cell to configure the charger driver. Signed-off-by: Timon Baetz Reviewed-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max8997.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 68d8f2b95287..55d3a6f97783 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -29,9 +29,9 @@ static const struct mfd_cell max8997_devs[] = { { .name = "max8997-pmic", }, { .name = "max8997-rtc", }, - { .name = "max8997-battery", }, + { .name = "max8997-battery", .of_compatible = "maxim,max8997-battery", }, { .name = "max8997-haptic", }, - { .name = "max8997-muic", }, + { .name = "max8997-muic", .of_compatible = "maxim,max8997-muic", }, { .name = "max8997-led", .id = 1 }, { .name = "max8997-led", .id = 2 }, }; From ab099cc6e5d488d60c72f41a696f5c8ec159f9f1 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Tue, 22 Dec 2020 21:33:26 +0800 Subject: [PATCH 17/32] mfd: mcp-sa11x0: Use DIV_ROUND_UP to calculate rw_timeout Don't open-code DIV_ROUND_UP() kernel macro. Signed-off-by: Zheng Yongjun Signed-off-by: Lee Jones --- drivers/mfd/mcp-sa11x0.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 98fa0af0e56e..4629dff187cd 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -214,8 +214,7 @@ static int mcp_sa11x0_probe(struct platform_device *dev) * rate. This is the period for 3 64-bit frames. Always * round this time up. */ - mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / - mcp->sclk_rate; + mcp->rw_timeout = DIV_ROUND_UP(64 * 3 * 1000000, mcp->sclk_rate); ret = mcp_host_add(mcp, data->codec_pdata); if (ret == 0) From db783e769a950b3f9502dfbc0f7fdbea499a1da6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 17 Dec 2020 08:34:20 +0000 Subject: [PATCH 18/32] mfd: Standardise MFD_CELL_* helper names Start all helpers with "MFD_CELL_". Cc: Gene Chen Cc: linux-mediatek@lists.infradead.org Signed-off-by: Lee Jones Reviewed-by: Linus Walleij Reviewed-by: Matthias Brugger Signed-off-by: Lee Jones --- drivers/mfd/ab8500-core.c | 42 +++++++++++++++++++------------------- drivers/mfd/db8500-prcmu.c | 6 +++--- drivers/mfd/mt6360-core.c | 12 +++++------ include/linux/mfd/core.h | 6 +++--- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index a3bac9da8cbb..ba8da061af0e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -610,52 +610,52 @@ int ab8500_suspend(struct ab8500 *ab8500) } static const struct mfd_cell ab8500_bm_devs[] = { - OF_MFD_CELL("ab8500-charger", NULL, &ab8500_bm_data, + MFD_CELL_OF("ab8500-charger", NULL, &ab8500_bm_data, sizeof(ab8500_bm_data), 0, "stericsson,ab8500-charger"), - OF_MFD_CELL("ab8500-btemp", NULL, &ab8500_bm_data, + MFD_CELL_OF("ab8500-btemp", NULL, &ab8500_bm_data, sizeof(ab8500_bm_data), 0, "stericsson,ab8500-btemp"), - OF_MFD_CELL("ab8500-fg", NULL, &ab8500_bm_data, + MFD_CELL_OF("ab8500-fg", NULL, &ab8500_bm_data, sizeof(ab8500_bm_data), 0, "stericsson,ab8500-fg"), - OF_MFD_CELL("ab8500-chargalg", NULL, &ab8500_bm_data, + MFD_CELL_OF("ab8500-chargalg", NULL, &ab8500_bm_data, sizeof(ab8500_bm_data), 0, "stericsson,ab8500-chargalg"), }; static const struct mfd_cell ab8500_devs[] = { #ifdef CONFIG_DEBUG_FS - OF_MFD_CELL("ab8500-debug", + MFD_CELL_OF("ab8500-debug", NULL, NULL, 0, 0, "stericsson,ab8500-debug"), #endif - OF_MFD_CELL("ab8500-sysctrl", + MFD_CELL_OF("ab8500-sysctrl", NULL, NULL, 0, 0, "stericsson,ab8500-sysctrl"), - OF_MFD_CELL("ab8500-ext-regulator", + MFD_CELL_OF("ab8500-ext-regulator", NULL, NULL, 0, 0, "stericsson,ab8500-ext-regulator"), - OF_MFD_CELL("ab8500-regulator", + MFD_CELL_OF("ab8500-regulator", NULL, NULL, 0, 0, "stericsson,ab8500-regulator"), - OF_MFD_CELL("ab8500-clk", + MFD_CELL_OF("ab8500-clk", NULL, NULL, 0, 0, "stericsson,ab8500-clk"), - OF_MFD_CELL("ab8500-gpadc", + MFD_CELL_OF("ab8500-gpadc", NULL, NULL, 0, 0, "stericsson,ab8500-gpadc"), - OF_MFD_CELL("ab8500-rtc", + MFD_CELL_OF("ab8500-rtc", NULL, NULL, 0, 0, "stericsson,ab8500-rtc"), - OF_MFD_CELL("ab8500-acc-det", + MFD_CELL_OF("ab8500-acc-det", NULL, NULL, 0, 0, "stericsson,ab8500-acc-det"), - OF_MFD_CELL("ab8500-poweron-key", + MFD_CELL_OF("ab8500-poweron-key", NULL, NULL, 0, 0, "stericsson,ab8500-poweron-key"), - OF_MFD_CELL("ab8500-pwm", + MFD_CELL_OF("ab8500-pwm", NULL, NULL, 0, 1, "stericsson,ab8500-pwm"), - OF_MFD_CELL("ab8500-pwm", + MFD_CELL_OF("ab8500-pwm", NULL, NULL, 0, 2, "stericsson,ab8500-pwm"), - OF_MFD_CELL("ab8500-pwm", + MFD_CELL_OF("ab8500-pwm", NULL, NULL, 0, 3, "stericsson,ab8500-pwm"), - OF_MFD_CELL("ab8500-denc", + MFD_CELL_OF("ab8500-denc", NULL, NULL, 0, 0, "stericsson,ab8500-denc"), - OF_MFD_CELL("pinctrl-ab8500", + MFD_CELL_OF("pinctrl-ab8500", NULL, NULL, 0, 0, "stericsson,ab8500-gpio"), - OF_MFD_CELL("abx500-temp", + MFD_CELL_OF("abx500-temp", NULL, NULL, 0, 0, "stericsson,abx500-temp"), - OF_MFD_CELL("ab8500-usb", + MFD_CELL_OF("ab8500-usb", NULL, NULL, 0, 0, "stericsson,ab8500-usb"), - OF_MFD_CELL("ab8500-codec", + MFD_CELL_OF("ab8500-codec", NULL, NULL, 0, 0, "stericsson,ab8500-codec"), }; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index a5983d515db0..167faac9b75b 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2954,12 +2954,12 @@ static const struct mfd_cell common_prcmu_devs[] = { }; static const struct mfd_cell db8500_prcmu_devs[] = { - OF_MFD_CELL("db8500-prcmu-regulators", NULL, + MFD_CELL_OF("db8500-prcmu-regulators", NULL, &db8500_regulators, sizeof(db8500_regulators), 0, "stericsson,db8500-prcmu-regulator"), - OF_MFD_CELL("cpuidle-dbx500", + MFD_CELL_OF("cpuidle-dbx500", NULL, NULL, 0, 0, "stericsson,cpuidle-dbx500"), - OF_MFD_CELL("db8500-thermal", + MFD_CELL_OF("db8500-thermal", NULL, NULL, 0, 0, "stericsson,db8500-thermal"), }; diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c index 4661c1b29a72..480722acf706 100644 --- a/drivers/mfd/mt6360-core.c +++ b/drivers/mfd/mt6360-core.c @@ -292,17 +292,17 @@ static const struct resource mt6360_ldo_resources[] = { }; static const struct mfd_cell mt6360_devs[] = { - OF_MFD_CELL("mt6360_adc", mt6360_adc_resources, + MFD_CELL_OF("mt6360_adc", mt6360_adc_resources, NULL, 0, 0, "mediatek,mt6360_adc"), - OF_MFD_CELL("mt6360_chg", mt6360_chg_resources, + MFD_CELL_OF("mt6360_chg", mt6360_chg_resources, NULL, 0, 0, "mediatek,mt6360_chg"), - OF_MFD_CELL("mt6360_led", mt6360_led_resources, + MFD_CELL_OF("mt6360_led", mt6360_led_resources, NULL, 0, 0, "mediatek,mt6360_led"), - OF_MFD_CELL("mt6360_pmic", mt6360_pmic_resources, + MFD_CELL_OF("mt6360_pmic", mt6360_pmic_resources, NULL, 0, 0, "mediatek,mt6360_pmic"), - OF_MFD_CELL("mt6360_ldo", mt6360_ldo_resources, + MFD_CELL_OF("mt6360_ldo", mt6360_ldo_resources, NULL, 0, 0, "mediatek,mt6360_ldo"), - OF_MFD_CELL("mt6360_tcpc", NULL, + MFD_CELL_OF("mt6360_tcpc", NULL, NULL, 0, 0, "mediatek,mt6360_tcpc"), }; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 4b35baa14d30..2009c4b936d9 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -28,13 +28,13 @@ .id = (_id), \ } -#define OF_MFD_CELL_REG(_name, _res, _pdata, _pdsize, _id, _compat, _of_reg) \ +#define MFD_CELL_OF_REG(_name, _res, _pdata, _pdsize, _id, _compat, _of_reg) \ MFD_CELL_ALL(_name, _res, _pdata, _pdsize, _id, _compat, _of_reg, true, NULL) -#define OF_MFD_CELL(_name, _res, _pdata, _pdsize, _id, _compat) \ +#define MFD_CELL_OF(_name, _res, _pdata, _pdsize, _id, _compat) \ MFD_CELL_ALL(_name, _res, _pdata, _pdsize, _id, _compat, 0, false, NULL) -#define ACPI_MFD_CELL(_name, _res, _pdata, _pdsize, _id, _match) \ +#define MFD_CELL_ACPI(_name, _res, _pdata, _pdsize, _id, _match) \ MFD_CELL_ALL(_name, _res, _pdata, _pdsize, _id, NULL, 0, false, _match) #define MFD_CELL_BASIC(_name, _res, _pdata, _pdsize, _id) \ From 8d9bf3c3e1451fc8de7b590040a868ade26d6b22 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 28 Dec 2020 13:10:04 -0800 Subject: [PATCH 19/32] mfd: gateworks-gsc: Fix interrupt type The Gateworks System Controller has an active-low interrupt. Fix the interrupt request type. Cc: Fixes: d85234994b2f ("mfd: Add Gateworks System Controller core driver") Signed-off-by: Tim Harvey Signed-off-by: Lee Jones --- drivers/mfd/gateworks-gsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c index 576da62fbb0c..d87876747b91 100644 --- a/drivers/mfd/gateworks-gsc.c +++ b/drivers/mfd/gateworks-gsc.c @@ -234,7 +234,7 @@ static int gsc_probe(struct i2c_client *client) ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq, IRQF_ONESHOT | IRQF_SHARED | - IRQF_TRIGGER_FALLING, 0, + IRQF_TRIGGER_LOW, 0, &gsc_irq_chip, &irq_data); if (ret) return ret; From f7b6732178e79ffb388aa343a7d0f63429d06204 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Jan 2021 17:39:25 +0200 Subject: [PATCH 20/32] mfd: intel-lpss: Add Intel Alder Lake PCH-P PCI IDs Add Intel Alder Lake LPSS PCI IDs. Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-pci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 2d7c588ef1ed..214994f50d3d 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -277,6 +277,19 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&spt_info }, + /* ADL-P */ + { PCI_VDEVICE(INTEL, 0x51a8), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51a9), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51c5), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51c6), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51c7), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51e8), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51e9), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51ea), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51eb), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&bxt_info }, /* APL */ { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info }, { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info }, From c239fea18e5763e88f3b7184a1c16b30fba57aac Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 28 Dec 2020 11:28:36 -0800 Subject: [PATCH 21/32] dt-bindings: mfd: gateworks-gsc: Add fan-tach mode In 7497d4a66c59 ("hwmon: (gsc-hwmon) add fan sensor") a mode was added to report RPM's from a fan tach input. Add this mode to the dt-bindings for the Gateworks System Controller. Signed-off-by: Tim Harvey Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml index d08e8fe76446..5a1e8d21f7a0 100644 --- a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml +++ b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml @@ -83,8 +83,9 @@ properties: 2 - scaled voltage based on an optional resistor divider and optional offset 3 - pre-scaled 16-bit voltage value + 4 - fan tach input to report RPM's $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3] + enum: [0, 1, 2, 3, 4] gw,voltage-divider-ohms: description: Values of resistors for divider on raw ADC input From 0eee3048202acb7f109face069c840146dfa2245 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 28 Dec 2020 19:05:45 +0300 Subject: [PATCH 22/32] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Add binding document for the ENE KB930 Embedded Controller. Reviewed-by: Rob Herring Signed-off-by: Dmitry Osipenko Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/ene-kb930.yaml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/ene-kb930.yaml diff --git a/Documentation/devicetree/bindings/mfd/ene-kb930.yaml b/Documentation/devicetree/bindings/mfd/ene-kb930.yaml new file mode 100644 index 000000000000..06ed9ec8f4bb --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ene-kb930.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ene-kb930.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ENE KB930 Embedded Controller bindings + +description: | + This binding describes the ENE KB930 Embedded Controller attached to an + I2C bus. + +maintainers: + - Dmitry Osipenko + +properties: + compatible: + items: + - enum: + - acer,a500-iconia-ec # Acer A500 Iconia tablet device + - const: ene,kb930 + reg: + maxItems: 1 + + monitored-battery: true + power-supplies: true + system-power-controller: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + battery: battery-cell { + compatible = "simple-battery"; + charge-full-design-microamp-hours = <3260000>; + energy-full-design-microwatt-hours = <24000000>; + operating-range-celsius = <0 40>; + }; + + mains: ac-adapter { + compatible = "gpio-charger"; + charger-type = "mains"; + gpios = <&gpio 125 0>; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + embedded-controller@58 { + compatible = "acer,a500-iconia-ec", "ene,kb930"; + reg = <0x58>; + + system-power-controller; + + monitored-battery = <&battery>; + power-supplies = <&mains>; + }; + }; + +... From 92eba6802c2b1ffb30f1454e9d99ef980b94bbbf Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 28 Dec 2020 19:05:46 +0300 Subject: [PATCH 23/32] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500 Acer Iconia Tab A500 is an Android tablet device, it has ENE KB930 Embedded Controller which provides battery-gauge, LED, GPIO and some other functions. The EC uses firmware that is specifically customized for Acer A500. This patch adds MFD driver for the Embedded Controller which allows to power-off / reboot the A500 device, it also provides a common register read/write API that will be used by the sub-devices. Signed-off-by: Dmitry Osipenko Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 11 ++ drivers/mfd/Makefile | 1 + drivers/mfd/acer-ec-a500.c | 202 +++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/mfd/acer-ec-a500.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 79274fc49f36..40c723e9a852 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2085,6 +2085,17 @@ config MFD_KHADAS_MCU additional drivers must be enabled in order to use the functionality of the device. +config MFD_ACER_A500_EC + tristate "Support for Acer Iconia Tab A500 Embedded Controller" + depends on I2C + depends on (ARCH_TEGRA_2x_SOC && OF) || COMPILE_TEST + select MFD_CORE + select REGMAP + help + Support for Embedded Controller found on Acer Iconia Tab A500. + The controller itself is ENE KB930, it is running firmware + customized for the specific needs of the Acer A500 hardware. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 14fdb188af02..025543418835 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -264,6 +264,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o obj-$(CONFIG_MFD_STMFX) += stmfx.o obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o +obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o diff --git a/drivers/mfd/acer-ec-a500.c b/drivers/mfd/acer-ec-a500.c new file mode 100644 index 000000000000..80c2fdd14fc4 --- /dev/null +++ b/drivers/mfd/acer-ec-a500.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Acer Iconia Tab A500 Embedded Controller Driver + * + * Copyright 2020 GRATE-driver project + */ + +#include +#include +#include +#include +#include +#include +#include + +#define A500_EC_I2C_ERR_TIMEOUT 500 +#define A500_EC_POWER_CMD_TIMEOUT 1000 + +/* + * Controller's firmware expects specific command opcodes to be used for the + * corresponding registers. Unsupported commands are skipped by the firmware. + */ +#define CMD_SHUTDOWN 0x0 +#define CMD_WARM_REBOOT 0x0 +#define CMD_COLD_REBOOT 0x1 + +enum { + REG_CURRENT_NOW = 0x03, + REG_SHUTDOWN = 0x52, + REG_WARM_REBOOT = 0x54, + REG_COLD_REBOOT = 0x55, +}; + +static struct i2c_client *a500_ec_client_pm_off; + +static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size, + void *val_buf, size_t val_sizel) +{ + struct i2c_client *client = context; + unsigned int reg, retries = 5; + u16 *ret_val = val_buf; + s32 ret = 0; + + reg = *(u8 *)reg_buf; + + while (retries-- > 0) { + ret = i2c_smbus_read_word_data(client, reg); + if (ret >= 0) + break; + + msleep(A500_EC_I2C_ERR_TIMEOUT); + } + + if (ret < 0) { + dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret); + return ret; + } + + *ret_val = ret; + + if (reg == REG_CURRENT_NOW) + fsleep(10000); + + return 0; +} + +static int a500_ec_write(void *context, const void *data, size_t count) +{ + struct i2c_client *client = context; + unsigned int reg, val, retries = 5; + s32 ret = 0; + + reg = *(u8 *)(data + 0); + val = *(u16 *)(data + 1); + + while (retries-- > 0) { + ret = i2c_smbus_write_word_data(client, reg, val); + if (ret >= 0) + break; + + msleep(A500_EC_I2C_ERR_TIMEOUT); + } + + if (ret < 0) { + dev_err(&client->dev, "write 0x%x failed: %d\n", reg, ret); + return ret; + } + + return 0; +} + +static const struct regmap_config a500_ec_regmap_config = { + .name = "KB930", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xff, +}; + +static const struct regmap_bus a500_ec_regmap_bus = { + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, + .write = a500_ec_write, + .read = a500_ec_read, + .max_raw_read = 2, +}; + +static void a500_ec_poweroff(void) +{ + i2c_smbus_write_word_data(a500_ec_client_pm_off, + REG_SHUTDOWN, CMD_SHUTDOWN); + + mdelay(A500_EC_POWER_CMD_TIMEOUT); +} + +static int a500_ec_restart_notify(struct notifier_block *this, + unsigned long reboot_mode, void *data) +{ + if (reboot_mode == REBOOT_WARM) + i2c_smbus_write_word_data(a500_ec_client_pm_off, + REG_WARM_REBOOT, CMD_WARM_REBOOT); + else + i2c_smbus_write_word_data(a500_ec_client_pm_off, + REG_COLD_REBOOT, CMD_COLD_REBOOT); + + mdelay(A500_EC_POWER_CMD_TIMEOUT); + + return NOTIFY_DONE; +} + +static struct notifier_block a500_ec_restart_handler = { + .notifier_call = a500_ec_restart_notify, + .priority = 200, +}; + +static const struct mfd_cell a500_ec_cells[] = { + { .name = "acer-a500-iconia-battery", }, + { .name = "acer-a500-iconia-leds", }, +}; + +static int a500_ec_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int err; + + regmap = devm_regmap_init(&client->dev, &a500_ec_regmap_bus, + client, &a500_ec_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + err = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, + a500_ec_cells, ARRAY_SIZE(a500_ec_cells), + NULL, 0, NULL); + if (err) { + dev_err(&client->dev, "failed to add sub-devices: %d\n", err); + return err; + } + + if (of_device_is_system_power_controller(client->dev.of_node)) { + a500_ec_client_pm_off = client; + + err = register_restart_handler(&a500_ec_restart_handler); + if (err) + return err; + + if (!pm_power_off) + pm_power_off = a500_ec_poweroff; + } + + return 0; +} + +static int a500_ec_remove(struct i2c_client *client) +{ + if (of_device_is_system_power_controller(client->dev.of_node)) { + if (pm_power_off == a500_ec_poweroff) + pm_power_off = NULL; + + unregister_restart_handler(&a500_ec_restart_handler); + } + + return 0; +} + +static const struct of_device_id a500_ec_match[] = { + { .compatible = "acer,a500-iconia-ec" }, + { } +}; +MODULE_DEVICE_TABLE(of, a500_ec_match); + +static struct i2c_driver a500_ec_driver = { + .driver = { + .name = "acer-a500-embedded-controller", + .of_match_table = a500_ec_match, + }, + .probe_new = a500_ec_probe, + .remove = a500_ec_remove, +}; +module_i2c_driver(a500_ec_driver); + +MODULE_DESCRIPTION("Acer Iconia Tab A500 Embedded Controller driver"); +MODULE_AUTHOR("Dmitry Osipenko "); +MODULE_LICENSE("GPL"); From 296f5568c6ee906e2a8db00adc751674f1745bd8 Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Thu, 14 Jan 2021 15:16:48 -0800 Subject: [PATCH 24/32] mfd: intel-m10-bmc: Expose MAC address and count Create two sysfs entries for exposing the MAC address and count from the MAX10 BMC register space. The MAC address is the first in a sequential block of MAC addresses reserved for the FPGA card. The MAC count is the number of MAC addresses in the reserved block. Signed-off-by: Russ Weight Signed-off-by: Xu Yilun Signed-off-by: Lee Jones --- .../ABI/testing/sysfs-driver-intel-m10-bmc | 21 +++++++++ drivers/mfd/intel-m10-bmc.c | 43 +++++++++++++++++++ include/linux/mfd/intel-m10-bmc.h | 9 ++++ 3 files changed, 73 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc b/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc index 979a2d62513f..9773925138af 100644 --- a/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc +++ b/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc @@ -13,3 +13,24 @@ Contact: Xu Yilun Description: Read only. Returns the firmware version of Intel MAX10 BMC chip. Format: "0x%x". + +What: /sys/bus/spi/devices/.../mac_address +Date: January 2021 +KernelVersion: 5.12 +Contact: Russ Weight +Description: Read only. Returns the first MAC address in a block + of sequential MAC addresses assigned to the board + that is managed by the Intel MAX10 BMC. It is stored in + FLASH storage and is mirrored in the MAX10 BMC register + space. + Format: "%02x:%02x:%02x:%02x:%02x:%02x". + +What: /sys/bus/spi/devices/.../mac_count +Date: January 2021 +KernelVersion: 5.12 +Contact: Russ Weight +Description: Read only. Returns the number of sequential MAC + addresses assigned to the board managed by the Intel + MAX10 BMC. This value is stored in FLASH and is mirrored + in the MAX10 BMC register space. + Format: "%u". diff --git a/drivers/mfd/intel-m10-bmc.c b/drivers/mfd/intel-m10-bmc.c index b84579b7b4f0..06c977519479 100644 --- a/drivers/mfd/intel-m10-bmc.c +++ b/drivers/mfd/intel-m10-bmc.c @@ -60,9 +60,52 @@ static ssize_t bmcfw_version_show(struct device *dev, } static DEVICE_ATTR_RO(bmcfw_version); +static ssize_t mac_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *max10 = dev_get_drvdata(dev); + unsigned int macaddr_low, macaddr_high; + int ret; + + ret = m10bmc_sys_read(max10, M10BMC_MAC_LOW, &macaddr_low); + if (ret) + return ret; + + ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + (u8)FIELD_GET(M10BMC_MAC_BYTE1, macaddr_low), + (u8)FIELD_GET(M10BMC_MAC_BYTE2, macaddr_low), + (u8)FIELD_GET(M10BMC_MAC_BYTE3, macaddr_low), + (u8)FIELD_GET(M10BMC_MAC_BYTE4, macaddr_low), + (u8)FIELD_GET(M10BMC_MAC_BYTE5, macaddr_high), + (u8)FIELD_GET(M10BMC_MAC_BYTE6, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_address); + +static ssize_t mac_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *max10 = dev_get_drvdata(dev); + unsigned int macaddr_high; + int ret; + + ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", + (u8)FIELD_GET(M10BMC_MAC_COUNT, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_count); + static struct attribute *m10bmc_attrs[] = { &dev_attr_bmc_version.attr, &dev_attr_bmcfw_version.attr, + &dev_attr_mac_address.attr, + &dev_attr_mac_count.attr, NULL, }; ATTRIBUTE_GROUPS(m10bmc); diff --git a/include/linux/mfd/intel-m10-bmc.h b/include/linux/mfd/intel-m10-bmc.h index c8ef2f1654a4..74d4e193966a 100644 --- a/include/linux/mfd/intel-m10-bmc.h +++ b/include/linux/mfd/intel-m10-bmc.h @@ -15,6 +15,15 @@ /* Register offset of system registers */ #define NIOS2_FW_VERSION 0x0 +#define M10BMC_MAC_LOW 0x10 +#define M10BMC_MAC_BYTE4 GENMASK(7, 0) +#define M10BMC_MAC_BYTE3 GENMASK(15, 8) +#define M10BMC_MAC_BYTE2 GENMASK(23, 16) +#define M10BMC_MAC_BYTE1 GENMASK(31, 24) +#define M10BMC_MAC_HIGH 0x14 +#define M10BMC_MAC_BYTE6 GENMASK(7, 0) +#define M10BMC_MAC_BYTE5 GENMASK(15, 8) +#define M10BMC_MAC_COUNT GENMASK(23, 16) #define M10BMC_TEST_REG 0x3c #define M10BMC_BUILD_VER 0x68 #define M10BMC_VER_MAJOR_MSK GENMASK(23, 16) From c7b79a75287141cef5bbaeaf1c942269c08cd52e Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 27 Oct 2020 12:46:16 +0200 Subject: [PATCH 25/32] mfd: intel-lpss: Add Intel Alder Lake PCH-S PCI IDs Add Intel Alder Lake LPSS PCI IDs. Signed-off-by: Jarkko Nikula Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-pci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 214994f50d3d..1522c8afc540 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -306,6 +306,21 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info }, + /* ADL-S */ + { PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7acc), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7acd), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7ace), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7acf), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7adc), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7afc), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7afd), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7afe), (kernel_ulong_t)&bxt_uart_info }, /* LKF */ { PCI_VDEVICE(INTEL, 0x98a8), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x98a9), (kernel_ulong_t)&bxt_uart_info }, From 8dc61152dfd2b5007ed0a00d2924b18a425ae605 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:03 -0600 Subject: [PATCH 26/32] mfd: iqs62x: Remove superfluous whitespace above fallthroughs Previously, all instances of the /* fall through */ comment were preceded by a newline to improve readability. Now that /* fall through */ comments have been replaced with the fallthrough pseudo-keyword, the leftover whitespace looks out of place and can simply be removed. Fixes: df561f6688fe ("treewide: Use fallthrough pseudo-keyword") Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 761b4ef3a381..ec4c79036515 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -135,7 +135,6 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) if (val & IQS620_PROX_SETTINGS_4_SAR_EN) iqs62x->ui_sel = IQS62X_UI_SAR1; - fallthrough; case IQS621_PROD_NUM: @@ -469,7 +468,6 @@ static irqreturn_t iqs62x_irq(int irq, void *context) switch (event_reg) { case IQS62X_EVENT_UI_LO: event_data.ui_data = get_unaligned_le16(&event_map[i]); - fallthrough; case IQS62X_EVENT_UI_HI: @@ -490,7 +488,6 @@ static irqreturn_t iqs62x_irq(int irq, void *context) case IQS62X_EVENT_HYST: event_map[i] <<= iqs62x->dev_desc->hyst_shift; - fallthrough; case IQS62X_EVENT_WHEEL: From 7ed645d54e07f03ba75486e1932ebb10266629c5 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:04 -0600 Subject: [PATCH 27/32] mfd: iqs62x: Remove unused bit mask The register write that performed a mandatory soft reset during probe was removed during development of the driver, however the IQS62X_SYS_SETTINGS_SOFT_RESET bit mask was left behind. Remove it to keep stray macros out of the driver. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index ec4c79036515..ff968dca23bb 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -57,7 +57,6 @@ #define IQS620_TEMP_CAL_OFFS 0xC4 #define IQS62X_SYS_SETTINGS 0xD0 -#define IQS62X_SYS_SETTINGS_SOFT_RESET BIT(7) #define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6) #define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5) #define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4) From f0c711266a540321a6099b05a3acb5385e81b53e Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:05 -0600 Subject: [PATCH 28/32] mfd: iqs62x: Rename regmap_config struct The regmap member of the driver's private data is called 'regmap', but the regmap_config struct is called 'iqs62x_map_config'. Rename the latter to 'iqs62x_regmap_config' for consistency. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index ff968dca23bb..7a1ff7c78801 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -866,7 +866,7 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { }, }; -static const struct regmap_config iqs62x_map_config = { +static const struct regmap_config iqs62x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = IQS62X_MAX_REG, @@ -892,7 +892,7 @@ static int iqs62x_probe(struct i2c_client *client) INIT_LIST_HEAD(&iqs62x->fw_blk_head); init_completion(&iqs62x->fw_done); - iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_map_config); + iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_regmap_config); if (IS_ERR(iqs62x->regmap)) { ret = PTR_ERR(iqs62x->regmap); dev_err(&client->dev, "Failed to initialize register map: %d\n", From a3a06ea17437814f47691282d9d6f6bcf62a8a9e Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:06 -0600 Subject: [PATCH 29/32] mfd: iqs62x: Increase interrupt handler return delay The time the device takes to deassert its RDY output following an I2C stop condition scales with the core clock frequency. To prevent level-triggered interrupts from being reasserted after the interrupt handler returns, increase the time before returning to account for the worst-case delay (~90 us) plus margin. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 7a1ff7c78801..07c972531715 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -533,7 +533,7 @@ static irqreturn_t iqs62x_irq(int irq, void *context) * ensure the device's RDY output has been deasserted by the time the * interrupt handler returns. */ - usleep_range(50, 100); + usleep_range(150, 200); return IRQ_HANDLED; } From 02e550d5b706f034d24e7e46234eb1982f05a137 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:07 -0600 Subject: [PATCH 30/32] mfd: iqs62x: Do not poll during ATI After loading firmware, the driver triggers ATI (calibration) with the newly loaded register configuration in place. Next, the driver polls a register field to ensure ATI completed in a timely fashion and that the device is ready to sense. However, communicating with the device over I2C while ATI is under- way may induce noise in the device and cause ATI to fail. As such, the vendor recommends not to poll the device during ATI. To solve this problem, let the device naturally signal to the host that ATI is complete by way of an interrupt. A completion prevents the sub-devices from being registered until this happens. The former logic that scaled ATI timeout and filter settling delay is not carried forward with the new implementation, as it produces overly conservative delays at lower clock rates. Instead, a single pair of delays that covers all cases is used. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 125 ++++++++++++++++++++----------------- include/linux/mfd/iqs62x.h | 11 ++-- 2 files changed, 73 insertions(+), 63 deletions(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 07c972531715..9b5c389ca2c3 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -36,7 +36,6 @@ #define IQS62X_PROD_NUM 0x00 #define IQS62X_SYS_FLAGS 0x10 -#define IQS62X_SYS_FLAGS_IN_ATI BIT(2) #define IQS620_HALL_FLAGS 0x16 #define IQS621_HALL_FLAGS 0x19 @@ -60,6 +59,7 @@ #define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6) #define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5) #define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4) +#define IQS62X_SYS_SETTINGS_COMM_ATI BIT(3) #define IQS62X_SYS_SETTINGS_REDO_ATI BIT(1) #define IQS62X_PWR_SETTINGS 0xD2 @@ -81,9 +81,7 @@ #define IQS62X_FW_REC_TYPE_MASK 3 #define IQS62X_FW_REC_TYPE_DATA 4 -#define IQS62X_ATI_POLL_SLEEP_US 10000 -#define IQS62X_ATI_POLL_TIMEOUT_US 500000 -#define IQS62X_ATI_STABLE_DELAY_MS 150 +#define IQS62X_FILT_SETTLE_MS 250 struct iqs62x_fw_rec { u8 type; @@ -111,7 +109,6 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) struct iqs62x_fw_blk *fw_blk; unsigned int val; int ret; - u8 clk_div = 1; list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) { if (fw_blk->mask) @@ -181,28 +178,32 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) return ret; } - ret = regmap_read(iqs62x->regmap, IQS62X_SYS_SETTINGS, &val); + /* + * Place the device in streaming mode at first so as not to miss the + * limited number of interrupts that would otherwise occur after ATI + * completes. The device is subsequently placed in event mode by the + * interrupt handler. + * + * In the meantime, mask interrupts during ATI to prevent the device + * from soliciting I2C traffic until the noise-sensitive ATI process + * is complete. + */ + ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, + IQS62X_SYS_SETTINGS_ACK_RESET | + IQS62X_SYS_SETTINGS_EVENT_MODE | + IQS62X_SYS_SETTINGS_COMM_ATI | + IQS62X_SYS_SETTINGS_REDO_ATI, + IQS62X_SYS_SETTINGS_ACK_RESET | + IQS62X_SYS_SETTINGS_REDO_ATI); if (ret) return ret; - if (val & IQS62X_SYS_SETTINGS_CLK_DIV) - clk_div = iqs62x->dev_desc->clk_div; - - ret = regmap_write(iqs62x->regmap, IQS62X_SYS_SETTINGS, val | - IQS62X_SYS_SETTINGS_ACK_RESET | - IQS62X_SYS_SETTINGS_EVENT_MODE | - IQS62X_SYS_SETTINGS_REDO_ATI); - if (ret) - return ret; - - ret = regmap_read_poll_timeout(iqs62x->regmap, IQS62X_SYS_FLAGS, val, - !(val & IQS62X_SYS_FLAGS_IN_ATI), - IQS62X_ATI_POLL_SLEEP_US, - IQS62X_ATI_POLL_TIMEOUT_US * clk_div); - if (ret) - return ret; - - msleep(IQS62X_ATI_STABLE_DELAY_MS * clk_div); + /* + * The following delay gives the device time to deassert its RDY output + * in case a communication window was open while the REDO_ATI field was + * written. This prevents an interrupt from being serviced prematurely. + */ + usleep_range(5000, 5100); return 0; } @@ -433,6 +434,11 @@ const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS] = { .mask = BIT(7), .val = BIT(7), }, + [IQS62X_EVENT_SYS_ATI] = { + .reg = IQS62X_EVENT_SYS, + .mask = BIT(2), + .val = BIT(2), + }, }; EXPORT_SYMBOL_GPL(iqs62x_events); @@ -521,12 +527,39 @@ static irqreturn_t iqs62x_irq(int irq, void *context) "Failed to re-initialize device: %d\n", ret); return IRQ_NONE; } + + iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_RESET); + reinit_completion(&iqs62x->ati_done); + } else if (event_flags & BIT(IQS62X_EVENT_SYS_ATI)) { + iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_ATI); + reinit_completion(&iqs62x->ati_done); + } else if (!completion_done(&iqs62x->ati_done)) { + ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, + IQS62X_SYS_SETTINGS_EVENT_MODE, 0xFF); + if (ret) { + dev_err(&client->dev, + "Failed to enable event mode: %d\n", ret); + return IRQ_NONE; + } + + msleep(IQS62X_FILT_SETTLE_MS); + complete_all(&iqs62x->ati_done); } - ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags, - &event_data); - if (ret & NOTIFY_STOP_MASK) - return IRQ_NONE; + /* + * Reset and ATI events are not broadcast to the sub-device drivers + * until ATI has completed. Any other events that may have occurred + * during ATI are ignored. + */ + if (completion_done(&iqs62x->ati_done)) { + event_flags |= iqs62x->event_cache; + ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags, + &event_data); + if (ret & NOTIFY_STOP_MASK) + return IRQ_NONE; + + iqs62x->event_cache = 0; + } /* * Once the communication window is closed, a small delay is added to @@ -567,6 +600,12 @@ static void iqs62x_firmware_load(const struct firmware *fw, void *context) goto err_out; } + if (!wait_for_completion_timeout(&iqs62x->ati_done, + msecs_to_jiffies(2000))) { + dev_err(&client->dev, "Failed to complete ATI\n"); + goto err_out; + } + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, iqs62x->dev_desc->sub_devs, iqs62x->dev_desc->num_sub_devs, @@ -748,22 +787,17 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs620at", .sub_devs = iqs620at_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs620at_sub_devs), - .prod_num = IQS620_PROD_NUM, .sw_num = 0x08, .cal_regs = iqs620at_cal_regs, .num_cal_regs = ARRAY_SIZE(iqs620at_cal_regs), - .prox_mask = BIT(0), .sar_mask = BIT(1) | BIT(7), .hall_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .prox_settings = IQS620_PROX_SETTINGS_4, .hall_flags = IQS620_HALL_FLAGS, - - .clk_div = 4, .fw_name = "iqs620a.bin", .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], }, @@ -771,20 +805,15 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs620a", .sub_devs = iqs620a_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs620a_sub_devs), - .prod_num = IQS620_PROD_NUM, .sw_num = 0x08, - .prox_mask = BIT(0), .sar_mask = BIT(1) | BIT(7), .hall_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .prox_settings = IQS620_PROX_SETTINGS_4, .hall_flags = IQS620_HALL_FLAGS, - - .clk_div = 4, .fw_name = "iqs620a.bin", .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], }, @@ -792,23 +821,18 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs621", .sub_devs = iqs621_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs621_sub_devs), - .prod_num = IQS621_PROD_NUM, .sw_num = 0x09, .cal_regs = iqs621_cal_regs, .num_cal_regs = ARRAY_SIZE(iqs621_cal_regs), - .prox_mask = BIT(0), .hall_mask = BIT(1), .als_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .als_flags = IQS621_ALS_FLAGS, .hall_flags = IQS621_HALL_FLAGS, .hyst_shift = 5, - - .clk_div = 2, .fw_name = "iqs621.bin", .event_regs = &iqs621_event_regs[IQS62X_UI_PROX], }, @@ -816,21 +840,16 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs622", .sub_devs = iqs622_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs622_sub_devs), - .prod_num = IQS622_PROD_NUM, .sw_num = 0x06, - .prox_mask = BIT(0), .sar_mask = BIT(1), .hall_mask = BIT(2), .als_mask = BIT(3), .ir_mask = BIT(4), - .prox_settings = IQS622_PROX_SETTINGS_4, .als_flags = IQS622_ALS_FLAGS, .hall_flags = IQS622_HALL_FLAGS, - - .clk_div = 2, .fw_name = "iqs622.bin", .event_regs = &iqs622_event_regs[IQS62X_UI_PROX], }, @@ -838,14 +857,10 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs624", .sub_devs = iqs624_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs624_sub_devs), - .prod_num = IQS624_PROD_NUM, .sw_num = 0x0B, - .interval = IQS624_INTERVAL_NUM, .interval_div = 3, - - .clk_div = 2, .fw_name = "iqs624.bin", .event_regs = &iqs624_event_regs[IQS62X_UI_PROX], }, @@ -853,14 +868,10 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs625", .sub_devs = iqs625_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs625_sub_devs), - .prod_num = IQS625_PROD_NUM, .sw_num = 0x0B, - .interval = IQS625_INTERVAL_NUM, .interval_div = 10, - - .clk_div = 2, .fw_name = "iqs625.bin", .event_regs = &iqs625_event_regs[IQS62X_UI_PROX], }, @@ -890,6 +901,8 @@ static int iqs62x_probe(struct i2c_client *client) BLOCKING_INIT_NOTIFIER_HEAD(&iqs62x->nh); INIT_LIST_HEAD(&iqs62x->fw_blk_head); + + init_completion(&iqs62x->ati_done); init_completion(&iqs62x->fw_done); iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_regmap_config); diff --git a/include/linux/mfd/iqs62x.h b/include/linux/mfd/iqs62x.h index 043d3b6de9ec..5ced55eae11b 100644 --- a/include/linux/mfd/iqs62x.h +++ b/include/linux/mfd/iqs62x.h @@ -28,7 +28,7 @@ #define IQS620_GLBL_EVENT_MASK_PMU BIT(6) #define IQS62X_NUM_KEYS 16 -#define IQS62X_NUM_EVENTS (IQS62X_NUM_KEYS + 5) +#define IQS62X_NUM_EVENTS (IQS62X_NUM_KEYS + 6) #define IQS62X_EVENT_SIZE 10 @@ -78,6 +78,7 @@ enum iqs62x_event_flag { /* everything else */ IQS62X_EVENT_SYS_RESET, + IQS62X_EVENT_SYS_ATI, }; struct iqs62x_event_data { @@ -97,12 +98,10 @@ struct iqs62x_dev_desc { const char *dev_name; const struct mfd_cell *sub_devs; int num_sub_devs; - u8 prod_num; u8 sw_num; const u8 *cal_regs; int num_cal_regs; - u8 prox_mask; u8 sar_mask; u8 hall_mask; @@ -110,16 +109,12 @@ struct iqs62x_dev_desc { u8 temp_mask; u8 als_mask; u8 ir_mask; - u8 prox_settings; u8 als_flags; u8 hall_flags; u8 hyst_shift; - u8 interval; u8 interval_div; - - u8 clk_div; const char *fw_name; const enum iqs62x_event_reg (*event_regs)[IQS62X_EVENT_SIZE]; }; @@ -130,8 +125,10 @@ struct iqs62x_core { struct regmap *regmap; struct blocking_notifier_head nh; struct list_head fw_blk_head; + struct completion ati_done; struct completion fw_done; enum iqs62x_ui_sel ui_sel; + unsigned long event_cache; }; extern const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS]; From 6a8fac01dc1c6508dd7cab876170024070b71039 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 17 Jan 2021 21:57:08 -0600 Subject: [PATCH 31/32] mfd: iqs62x: Do not change clock frequency during ATI After a reset event, the device automatically triggers ATI at the default core clock frequency (16 MHz). Soon afterward, the driver loads firmware which may attempt to lower the frequency. If this initial ATI cycle is still in progress when the frequency is changed, however, the device incorrectly reports channels 0, 1 and 2 to be in a state of touch once ATI finally completes. To solve this problem, wait until ATI is complete before lowering the frequency. Because this particular ATI cycle occurs following a reset event, its duration is predictable and a simple delay can suffice. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 9b5c389ca2c3..d1fc38a78acb 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -81,6 +81,7 @@ #define IQS62X_FW_REC_TYPE_MASK 3 #define IQS62X_FW_REC_TYPE_DATA 4 +#define IQS62X_ATI_STARTUP_MS 350 #define IQS62X_FILT_SETTLE_MS 250 struct iqs62x_fw_rec { @@ -111,6 +112,14 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) int ret; list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) { + /* + * In case ATI is in progress, wait for it to complete before + * lowering the core clock frequency. + */ + if (fw_blk->addr == IQS62X_SYS_SETTINGS && + *fw_blk->data & IQS62X_SYS_SETTINGS_CLK_DIV) + msleep(IQS62X_ATI_STARTUP_MS); + if (fw_blk->mask) ret = regmap_update_bits(iqs62x->regmap, fw_blk->addr, fw_blk->mask, *fw_blk->data); From 26783d74cc6a440ee3ef9836a008a697981013d0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 29 Jan 2021 17:37:24 +0300 Subject: [PATCH 32/32] mfd: wm831x-auxadc: Prevent use after free in wm831x_auxadc_read_irq() The "req" struct is always added to the "wm831x->auxadc_pending" list, but it's only removed from the list on the success path. If a failure occurs then the "req" struct is freed but it's still on the list, leading to a use after free. Fixes: 78bb3688ea18 ("mfd: Support multiple active WM831x AUXADC conversions") Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm831x-auxadc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 8a7cc0f86958..65b98f3fbd92 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -93,11 +93,10 @@ static int wm831x_auxadc_read_irq(struct wm831x *wm831x, wait_for_completion_timeout(&req->done, msecs_to_jiffies(500)); mutex_lock(&wm831x->auxadc_lock); - - list_del(&req->list); ret = req->val; out: + list_del(&req->list); mutex_unlock(&wm831x->auxadc_lock); kfree(req);