From 1852f5ed358147095297a09cc3c6f160208a676d Mon Sep 17 00:00:00 2001 From: Jeongtae Park Date: Thu, 1 Jul 2021 23:26:30 +0900 Subject: [PATCH 001/177] regmap: fix the offset of register error log This patch fixes the offset of register error log by using regmap_get_offset(). Signed-off-by: Jeongtae Park Link: https://lore.kernel.org/r/20210701142630.44936-1-jeongtae.park@gmail.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index fe3e38dd5324..2fc826e97591 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1667,7 +1667,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, if (ret) { dev_err(map->dev, "Error in caching of register: %x ret: %d\n", - reg + i, ret); + reg + regmap_get_offset(map, i), ret); return ret; } } From d63aa09f7c53bdeb83edb4d84c07d759a92223bb Mon Sep 17 00:00:00 2001 From: Jinchao Wang Date: Tue, 29 Jun 2021 01:19:06 +0800 Subject: [PATCH 002/177] regmap: Prefer unsigned int to bare use of unsigned Fix checkpatch warnings: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' Signed-off-by: Jinchao Wang Link: https://lore.kernel.org/r/20210628171907.63646-1-wjc@cdjrlc.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 2 +- drivers/base/regmap/regmap-mmio.c | 2 +- drivers/base/regmap/regmap.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 211a335a608d..ad684d37c2da 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -368,7 +368,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, char *buf; char *entry; int ret; - unsigned entry_len; + unsigned int entry_len; if (*ppos < 0 || !count) return -EINVAL; diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index f9cd51afb9d2..71f16be7e717 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -15,7 +15,7 @@ struct regmap_mmio_context { void __iomem *regs; - unsigned val_bytes; + unsigned int val_bytes; bool relaxed_mmio; bool attached_clk; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 2fc826e97591..dcfa99ea7f31 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1126,10 +1126,10 @@ skip_format_initialization: /* Make sure, that this register range has no selector or data window within its boundary */ for (j = 0; j < config->num_ranges; j++) { - unsigned sel_reg = config->ranges[j].selector_reg; - unsigned win_min = config->ranges[j].window_start; - unsigned win_max = win_min + - config->ranges[j].window_len - 1; + unsigned int sel_reg = config->ranges[j].selector_reg; + unsigned int win_min = config->ranges[j].window_start; + unsigned int win_max = win_min + + config->ranges[j].window_len - 1; /* Allow data window inside its own virtual range */ if (j == i) From e8608f8956ff4015a206f75631a266d93e84bb2d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 2 Jul 2021 09:37:17 +0200 Subject: [PATCH 003/177] regulator: hi6421v600-regulator: add a missing dot at copyright The Huawei's copyright is missing a dot at the end. Add it, in order to make it similar to the other two copyrights. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/e9c0f27688d7be75652800e67c913878fd772cbe.1625211021.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/hi6421v600-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/hi6421v600-regulator.c b/drivers/regulator/hi6421v600-regulator.c index 845bc3b4026d..916d4e1f9061 100644 --- a/drivers/regulator/hi6421v600-regulator.c +++ b/drivers/regulator/hi6421v600-regulator.c @@ -4,7 +4,7 @@ // // Copyright (c) 2013 Linaro Ltd. // Copyright (c) 2011 HiSilicon Ltd. -// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd +// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd. // // Guodong Xu From 4ff75a29976590bc7afe3ed75d547c1f2a924c75 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 25 Jun 2021 15:23:22 +0300 Subject: [PATCH 004/177] regulator: devres: remove devm_regulator_unregister() function This API hook isn't used anywhere and most-likely exists because of the general principle of C APIs, where if an API function does an allocation/registration, it must also have an equivalent deallocation/deregistration counterpart. For devm_ functions this isn't all that true (for all cases), as the idea of these function is to provide an auto-cleanup logic on drivers/system de-init. Removing this also discourages any weird logic that could be created with such an API function. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210625122324.327585-3-aardelean@deviqon.com Signed-off-by: Mark Brown --- drivers/regulator/devres.c | 29 ----------------------------- include/linux/regulator/driver.h | 1 - 2 files changed, 30 deletions(-) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index a8de0aa88bad..79e2571113b6 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -205,35 +205,6 @@ struct regulator_dev *devm_regulator_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regulator_register); -static int devm_rdev_match(struct device *dev, void *res, void *data) -{ - struct regulator_dev **r = res; - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - return *r == data; -} - -/** - * devm_regulator_unregister - Resource managed regulator_unregister() - * @dev: device to supply - * @rdev: regulator to free - * - * Unregister a regulator registered with devm_regulator_register(). - * Normally this function will not need to be called and the resource - * management code will ensure that the resource is freed. - */ -void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) -{ - int rc; - - rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev); - if (rc != 0) - WARN_ON(rc); -} -EXPORT_SYMBOL_GPL(devm_regulator_unregister); - struct regulator_supply_alias_match { struct device *dev; const char *id; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 4aec20387857..5447a6b33fa0 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -645,7 +645,6 @@ devm_regulator_register(struct device *dev, const struct regulator_desc *regulator_desc, const struct regulator_config *config); void regulator_unregister(struct regulator_dev *rdev); -void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); From eed43b96ede9c3f018ad24149de83f24b86ad729 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 25 Jun 2021 15:23:23 +0300 Subject: [PATCH 005/177] regulator: devres: remove devm_regulator_bulk_unregister_supply_alias() This API hook isn't used anywhere and most-likely exists because of the general principle of C APIs, where if an API function does an allocation/registration, it must also have an equivalent deallocation/deregistration counterpart. For devm_ functions this isn't all that true (for all cases), as the idea of these function is to provide an auto-cleanup logic on drivers/system de-init. Removing this also discourages any weird logic that could be created with such an API function. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210625122324.327585-4-aardelean@deviqon.com Signed-off-by: Mark Brown --- drivers/regulator/devres.c | 24 ------------------------ include/linux/regulator/consumer.h | 8 -------- 2 files changed, 32 deletions(-) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 79e2571113b6..6c657b29a6e1 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -344,30 +344,6 @@ err: } EXPORT_SYMBOL_GPL(devm_regulator_bulk_register_supply_alias); -/** - * devm_regulator_bulk_unregister_supply_alias - Managed unregister - * multiple aliases - * - * @dev: device to supply - * @id: list of supply names or regulator IDs - * @num_id: number of aliases to unregister - * - * Unregister aliases registered with - * devm_regulator_bulk_register_supply_alias(). Normally this function - * will not need to be called and the resource management code - * will ensure that the resource is freed. - */ -void devm_regulator_bulk_unregister_supply_alias(struct device *dev, - const char *const *id, - int num_id) -{ - int i; - - for (i = 0; i < num_id; ++i) - devm_regulator_unregister_supply_alias(dev, id[i]); -} -EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias); - struct regulator_notifier_match { struct regulator *regulator; struct notifier_block *nb; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index f72ca73631be..98518b3f2828 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -230,9 +230,6 @@ int devm_regulator_bulk_register_supply_alias(struct device *dev, struct device *alias_dev, const char *const *alias_id, int num_id); -void devm_regulator_bulk_unregister_supply_alias(struct device *dev, - const char *const *id, - int num_id); /* regulator output control and status */ int __must_check regulator_enable(struct regulator *regulator); @@ -422,11 +419,6 @@ static inline int devm_regulator_bulk_register_supply_alias(struct device *dev, return 0; } -static inline void devm_regulator_bulk_unregister_supply_alias( - struct device *dev, const char *const *id, int num_id) -{ -} - static inline int regulator_enable(struct regulator *regulator) { return 0; From 4d9f4d1de3ceb84fa6ce68177a26b8fac6a71290 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 25 Jun 2021 15:23:24 +0300 Subject: [PATCH 006/177] regulator: devres: unexport devm_regulator_unregister_supply_alias() This API hook isn't used anywhere outside of the regulator devres code. This function is needed for the devm_regulator_bulk_register_supply_alias() function on the error path, to cleanup any previously registered supply aliases. This change makes the devm_regulator_unregister_supply_alias() local to the regulator core framework, to avoid it being used in any weird logic. It's also removing the doc-string for devm_regulator_unregister_supply_alias(), since it doesn't need to be documented anymore, as no other external consumer should use it. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210625122324.327585-5-aardelean@deviqon.com Signed-off-by: Mark Brown --- drivers/regulator/devres.c | 16 ++-------------- include/linux/regulator/consumer.h | 7 ------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 6c657b29a6e1..9113233f41cd 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -267,19 +267,8 @@ int devm_regulator_register_supply_alias(struct device *dev, const char *id, } EXPORT_SYMBOL_GPL(devm_regulator_register_supply_alias); -/** - * devm_regulator_unregister_supply_alias - Resource managed - * regulator_unregister_supply_alias() - * - * @dev: device to supply - * @id: supply name or regulator ID - * - * Unregister an alias registered with - * devm_regulator_register_supply_alias(). Normally this function - * will not need to be called and the resource management code - * will ensure that the resource is freed. - */ -void devm_regulator_unregister_supply_alias(struct device *dev, const char *id) +static void devm_regulator_unregister_supply_alias(struct device *dev, + const char *id) { struct regulator_supply_alias_match match; int rc; @@ -292,7 +281,6 @@ void devm_regulator_unregister_supply_alias(struct device *dev, const char *id) if (rc != 0) WARN_ON(rc); } -EXPORT_SYMBOL_GPL(devm_regulator_unregister_supply_alias); /** * devm_regulator_bulk_register_supply_alias - Managed register diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 98518b3f2828..bbf6590a6dec 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -222,8 +222,6 @@ void regulator_bulk_unregister_supply_alias(struct device *dev, int devm_regulator_register_supply_alias(struct device *dev, const char *id, struct device *alias_dev, const char *alias_id); -void devm_regulator_unregister_supply_alias(struct device *dev, - const char *id); int devm_regulator_bulk_register_supply_alias(struct device *dev, const char *const *id, @@ -405,11 +403,6 @@ static inline int devm_regulator_register_supply_alias(struct device *dev, return 0; } -static inline void devm_regulator_unregister_supply_alias(struct device *dev, - const char *id) -{ -} - static inline int devm_regulator_bulk_register_supply_alias(struct device *dev, const char *const *id, struct device *alias_dev, From b99b7b79a7c57d5f1550b9a507521c791c5f92ed Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 29 Jun 2021 21:05:01 +0800 Subject: [PATCH 007/177] regulator: mt6358: Remove shift fields from struct mt6358_regulator_info The shift setting can be calculated via the corresponding mask field, so remove these shift fields. The usage of da_vsel_mask is different from other mask defines because current code does shift regval before mask with the da_vsel_mask. Do proper shit to da_vsel_mask setting so we can calculate the shift. Signed-off-by: Axel Lin Link: https://lore.kernel.org/r/20210629130503.2183574-1-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/mt6358-regulator.c | 87 +++++++++++++--------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c index 0d35be4e0e5a..eb8027813b99 100644 --- a/drivers/regulator/mt6358-regulator.c +++ b/drivers/regulator/mt6358-regulator.c @@ -28,18 +28,15 @@ struct mt6358_regulator_info { u32 qi; const u32 *index_table; unsigned int n_table; - u32 vsel_shift; u32 da_vsel_reg; u32 da_vsel_mask; - u32 da_vsel_shift; u32 modeset_reg; u32 modeset_mask; - u32 modeset_shift; }; #define MT6358_BUCK(match, vreg, min, max, step, \ volt_ranges, vosel_mask, _da_vsel_reg, _da_vsel_mask, \ - _da_vsel_shift, _modeset_reg, _modeset_shift) \ + _modeset_reg, _modeset_shift) \ [MT6358_ID_##vreg] = { \ .desc = { \ .name = #vreg, \ @@ -61,15 +58,13 @@ struct mt6358_regulator_info { .qi = BIT(0), \ .da_vsel_reg = _da_vsel_reg, \ .da_vsel_mask = _da_vsel_mask, \ - .da_vsel_shift = _da_vsel_shift, \ .modeset_reg = _modeset_reg, \ .modeset_mask = BIT(_modeset_shift), \ - .modeset_shift = _modeset_shift \ } #define MT6358_LDO(match, vreg, ldo_volt_table, \ ldo_index_table, enreg, enbit, vosel, \ - vosel_mask, vosel_shift) \ + vosel_mask) \ [MT6358_ID_##vreg] = { \ .desc = { \ .name = #vreg, \ @@ -89,12 +84,11 @@ struct mt6358_regulator_info { .qi = BIT(15), \ .index_table = ldo_index_table, \ .n_table = ARRAY_SIZE(ldo_index_table), \ - .vsel_shift = vosel_shift, \ } #define MT6358_LDO1(match, vreg, min, max, step, \ volt_ranges, _da_vsel_reg, _da_vsel_mask, \ - _da_vsel_shift, vosel, vosel_mask) \ + vosel, vosel_mask) \ [MT6358_ID_##vreg] = { \ .desc = { \ .name = #vreg, \ @@ -113,7 +107,6 @@ struct mt6358_regulator_info { }, \ .da_vsel_reg = _da_vsel_reg, \ .da_vsel_mask = _da_vsel_mask, \ - .da_vsel_shift = _da_vsel_shift, \ .status_reg = MT6358_LDO_##vreg##_DBG1, \ .qi = BIT(0), \ } @@ -260,9 +253,9 @@ static int mt6358_set_voltage_sel(struct regulator_dev *rdev, pvol = info->index_table; idx = pvol[selector]; + idx <<= ffs(info->desc.vsel_mask) - 1; ret = regmap_update_bits(rdev->regmap, info->desc.vsel_reg, - info->desc.vsel_mask, - idx << info->vsel_shift); + info->desc.vsel_mask, idx); return ret; } @@ -282,7 +275,8 @@ static int mt6358_get_voltage_sel(struct regulator_dev *rdev) return ret; } - selector = (selector & info->desc.vsel_mask) >> info->vsel_shift; + selector = (selector & info->desc.vsel_mask) >> + (ffs(info->desc.vsel_mask) - 1); pvol = info->index_table; for (idx = 0; idx < info->desc.n_voltages; idx++) { if (pvol[idx] == selector) @@ -305,7 +299,7 @@ static int mt6358_get_buck_voltage_sel(struct regulator_dev *rdev) return ret; } - ret = (regval >> info->da_vsel_shift) & info->da_vsel_mask; + ret = (regval & info->da_vsel_mask) >> (ffs(info->da_vsel_mask) - 1); return ret; } @@ -342,11 +336,10 @@ static int mt6358_regulator_set_mode(struct regulator_dev *rdev, return -EINVAL; } - dev_dbg(&rdev->dev, "mt6358 buck set_mode %#x, %#x, %#x, %#x\n", - info->modeset_reg, info->modeset_mask, - info->modeset_shift, val); + dev_dbg(&rdev->dev, "mt6358 buck set_mode %#x, %#x, %#x\n", + info->modeset_reg, info->modeset_mask, val); - val <<= info->modeset_shift; + val <<= ffs(info->modeset_mask) - 1; return regmap_update_bits(rdev->regmap, info->modeset_reg, info->modeset_mask, val); @@ -364,7 +357,7 @@ static unsigned int mt6358_regulator_get_mode(struct regulator_dev *rdev) return ret; } - switch ((regval & info->modeset_mask) >> info->modeset_shift) { + switch ((regval & info->modeset_mask) >> (ffs(info->modeset_mask) - 1)) { case MT6358_BUCK_MODE_AUTO: return REGULATOR_MODE_NORMAL; case MT6358_BUCK_MODE_FORCE_PWM: @@ -412,30 +405,30 @@ static const struct regulator_ops mt6358_volt_fixed_ops = { static struct mt6358_regulator_info mt6358_regulators[] = { MT6358_BUCK("buck_vdram1", VDRAM1, 500000, 2087500, 12500, buck_volt_range2, 0x7f, MT6358_BUCK_VDRAM1_DBG0, 0x7f, - 0, MT6358_VDRAM1_ANA_CON0, 8), + MT6358_VDRAM1_ANA_CON0, 8), MT6358_BUCK("buck_vcore", VCORE, 500000, 1293750, 6250, buck_volt_range1, 0x7f, MT6358_BUCK_VCORE_DBG0, 0x7f, - 0, MT6358_VCORE_VGPU_ANA_CON0, 1), + MT6358_VCORE_VGPU_ANA_CON0, 1), MT6358_BUCK("buck_vpa", VPA, 500000, 3650000, 50000, - buck_volt_range3, 0x3f, MT6358_BUCK_VPA_DBG0, 0x3f, 0, + buck_volt_range3, 0x3f, MT6358_BUCK_VPA_DBG0, 0x3f, MT6358_VPA_ANA_CON0, 3), MT6358_BUCK("buck_vproc11", VPROC11, 500000, 1293750, 6250, buck_volt_range1, 0x7f, MT6358_BUCK_VPROC11_DBG0, 0x7f, - 0, MT6358_VPROC_ANA_CON0, 1), + MT6358_VPROC_ANA_CON0, 1), MT6358_BUCK("buck_vproc12", VPROC12, 500000, 1293750, 6250, buck_volt_range1, 0x7f, MT6358_BUCK_VPROC12_DBG0, 0x7f, - 0, MT6358_VPROC_ANA_CON0, 2), + MT6358_VPROC_ANA_CON0, 2), MT6358_BUCK("buck_vgpu", VGPU, 500000, 1293750, 6250, - buck_volt_range1, 0x7f, MT6358_BUCK_VGPU_ELR0, 0x7f, 0, + buck_volt_range1, 0x7f, MT6358_BUCK_VGPU_ELR0, 0x7f, MT6358_VCORE_VGPU_ANA_CON0, 2), MT6358_BUCK("buck_vs2", VS2, 500000, 2087500, 12500, - buck_volt_range2, 0x7f, MT6358_BUCK_VS2_DBG0, 0x7f, 0, + buck_volt_range2, 0x7f, MT6358_BUCK_VS2_DBG0, 0x7f, MT6358_VS2_ANA_CON0, 8), MT6358_BUCK("buck_vmodem", VMODEM, 500000, 1293750, 6250, buck_volt_range1, 0x7f, MT6358_BUCK_VMODEM_DBG0, 0x7f, - 0, MT6358_VMODEM_ANA_CON0, 8), + MT6358_VMODEM_ANA_CON0, 8), MT6358_BUCK("buck_vs1", VS1, 1000000, 2587500, 12500, - buck_volt_range4, 0x7f, MT6358_BUCK_VS1_DBG0, 0x7f, 0, + buck_volt_range4, 0x7f, MT6358_BUCK_VS1_DBG0, 0x7f, MT6358_VS1_ANA_CON0, 8), MT6358_REG_FIXED("ldo_vrf12", VRF12, MT6358_LDO_VRF12_CON0, 0, 1200000), @@ -457,49 +450,49 @@ static struct mt6358_regulator_info mt6358_regulators[] = { MT6358_REG_FIXED("ldo_vaud28", VAUD28, MT6358_LDO_VAUD28_CON0, 0, 2800000), MT6358_LDO("ldo_vdram2", VDRAM2, vdram2_voltages, vdram2_idx, - MT6358_LDO_VDRAM2_CON0, 0, MT6358_LDO_VDRAM2_ELR0, 0xf, 0), + MT6358_LDO_VDRAM2_CON0, 0, MT6358_LDO_VDRAM2_ELR0, 0xf), MT6358_LDO("ldo_vsim1", VSIM1, vsim_voltages, vsim_idx, - MT6358_LDO_VSIM1_CON0, 0, MT6358_VSIM1_ANA_CON0, 0xf00, 8), + MT6358_LDO_VSIM1_CON0, 0, MT6358_VSIM1_ANA_CON0, 0xf00), MT6358_LDO("ldo_vibr", VIBR, vibr_voltages, vibr_idx, - MT6358_LDO_VIBR_CON0, 0, MT6358_VIBR_ANA_CON0, 0xf00, 8), + MT6358_LDO_VIBR_CON0, 0, MT6358_VIBR_ANA_CON0, 0xf00), MT6358_LDO("ldo_vusb", VUSB, vusb_voltages, vusb_idx, - MT6358_LDO_VUSB_CON0_0, 0, MT6358_VUSB_ANA_CON0, 0x700, 8), + MT6358_LDO_VUSB_CON0_0, 0, MT6358_VUSB_ANA_CON0, 0x700), MT6358_LDO("ldo_vcamd", VCAMD, vcamd_voltages, vcamd_idx, - MT6358_LDO_VCAMD_CON0, 0, MT6358_VCAMD_ANA_CON0, 0xf00, 8), + MT6358_LDO_VCAMD_CON0, 0, MT6358_VCAMD_ANA_CON0, 0xf00), MT6358_LDO("ldo_vefuse", VEFUSE, vefuse_voltages, vefuse_idx, - MT6358_LDO_VEFUSE_CON0, 0, MT6358_VEFUSE_ANA_CON0, 0xf00, 8), + MT6358_LDO_VEFUSE_CON0, 0, MT6358_VEFUSE_ANA_CON0, 0xf00), MT6358_LDO("ldo_vmch", VMCH, vmch_vemc_voltages, vmch_vemc_idx, - MT6358_LDO_VMCH_CON0, 0, MT6358_VMCH_ANA_CON0, 0x700, 8), + MT6358_LDO_VMCH_CON0, 0, MT6358_VMCH_ANA_CON0, 0x700), MT6358_LDO("ldo_vcama1", VCAMA1, vcama_voltages, vcama_idx, - MT6358_LDO_VCAMA1_CON0, 0, MT6358_VCAMA1_ANA_CON0, 0xf00, 8), + MT6358_LDO_VCAMA1_CON0, 0, MT6358_VCAMA1_ANA_CON0, 0xf00), MT6358_LDO("ldo_vemc", VEMC, vmch_vemc_voltages, vmch_vemc_idx, - MT6358_LDO_VEMC_CON0, 0, MT6358_VEMC_ANA_CON0, 0x700, 8), + MT6358_LDO_VEMC_CON0, 0, MT6358_VEMC_ANA_CON0, 0x700), MT6358_LDO("ldo_vcn33_bt", VCN33_BT, vcn33_bt_wifi_voltages, vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_0, - 0, MT6358_VCN33_ANA_CON0, 0x300, 8), + 0, MT6358_VCN33_ANA_CON0, 0x300), MT6358_LDO("ldo_vcn33_wifi", VCN33_WIFI, vcn33_bt_wifi_voltages, vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_1, - 0, MT6358_VCN33_ANA_CON0, 0x300, 8), + 0, MT6358_VCN33_ANA_CON0, 0x300), MT6358_LDO("ldo_vcama2", VCAMA2, vcama_voltages, vcama_idx, - MT6358_LDO_VCAMA2_CON0, 0, MT6358_VCAMA2_ANA_CON0, 0xf00, 8), + MT6358_LDO_VCAMA2_CON0, 0, MT6358_VCAMA2_ANA_CON0, 0xf00), MT6358_LDO("ldo_vmc", VMC, vmc_voltages, vmc_idx, - MT6358_LDO_VMC_CON0, 0, MT6358_VMC_ANA_CON0, 0xf00, 8), + MT6358_LDO_VMC_CON0, 0, MT6358_VMC_ANA_CON0, 0xf00), MT6358_LDO("ldo_vldo28", VLDO28, vldo28_voltages, vldo28_idx, MT6358_LDO_VLDO28_CON0_0, 0, - MT6358_VLDO28_ANA_CON0, 0x300, 8), + MT6358_VLDO28_ANA_CON0, 0x300), MT6358_LDO("ldo_vsim2", VSIM2, vsim_voltages, vsim_idx, - MT6358_LDO_VSIM2_CON0, 0, MT6358_VSIM2_ANA_CON0, 0xf00, 8), + MT6358_LDO_VSIM2_CON0, 0, MT6358_VSIM2_ANA_CON0, 0xf00), MT6358_LDO1("ldo_vsram_proc11", VSRAM_PROC11, 500000, 1293750, 6250, - buck_volt_range1, MT6358_LDO_VSRAM_PROC11_DBG0, 0x7f, 8, + buck_volt_range1, MT6358_LDO_VSRAM_PROC11_DBG0, 0x7f00, MT6358_LDO_VSRAM_CON0, 0x7f), MT6358_LDO1("ldo_vsram_others", VSRAM_OTHERS, 500000, 1293750, 6250, - buck_volt_range1, MT6358_LDO_VSRAM_OTHERS_DBG0, 0x7f, 8, + buck_volt_range1, MT6358_LDO_VSRAM_OTHERS_DBG0, 0x7f00, MT6358_LDO_VSRAM_CON2, 0x7f), MT6358_LDO1("ldo_vsram_gpu", VSRAM_GPU, 500000, 1293750, 6250, - buck_volt_range1, MT6358_LDO_VSRAM_GPU_DBG0, 0x7f, 8, + buck_volt_range1, MT6358_LDO_VSRAM_GPU_DBG0, 0x7f00, MT6358_LDO_VSRAM_CON3, 0x7f), MT6358_LDO1("ldo_vsram_proc12", VSRAM_PROC12, 500000, 1293750, 6250, - buck_volt_range1, MT6358_LDO_VSRAM_PROC12_DBG0, 0x7f, 8, + buck_volt_range1, MT6358_LDO_VSRAM_PROC12_DBG0, 0x7f00, MT6358_LDO_VSRAM_CON1, 0x7f), }; From d6208ba87066c981589ca41f07d29a5803807ead Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 29 Jun 2021 21:05:02 +0800 Subject: [PATCH 008/177] regulator: mt6359: Remove shift fields from struct mt6359_regulator_info The shift setting can be calculated via the corresponding mask field, so remove these shift fields. Signed-off-by: Axel Lin Link: https://lore.kernel.org/r/20210629130503.2183574-2-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/mt6359-regulator.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/mt6359-regulator.c b/drivers/regulator/mt6359-regulator.c index 7ce0bd377a08..de3b0462832c 100644 --- a/drivers/regulator/mt6359-regulator.c +++ b/drivers/regulator/mt6359-regulator.c @@ -27,7 +27,6 @@ * @qi: Mask for query enable signal status of regulators. * @modeset_reg: for operating AUTO/PWM mode register. * @modeset_mask: MASK for operating modeset register. - * @modeset_shift: SHIFT for operating modeset register. */ struct mt6359_regulator_info { struct regulator_desc desc; @@ -35,10 +34,8 @@ struct mt6359_regulator_info { u32 qi; u32 modeset_reg; u32 modeset_mask; - u32 modeset_shift; u32 lp_mode_reg; u32 lp_mode_mask; - u32 lp_mode_shift; }; #define MT6359_BUCK(match, _name, min, max, step, \ @@ -68,10 +65,8 @@ struct mt6359_regulator_info { .qi = BIT(0), \ .lp_mode_reg = _lp_mode_reg, \ .lp_mode_mask = BIT(_lp_mode_shift), \ - .lp_mode_shift = _lp_mode_shift, \ .modeset_reg = _modeset_reg, \ .modeset_mask = BIT(_modeset_shift), \ - .modeset_shift = _modeset_shift \ } #define MT6359_LDO_LINEAR(match, _name, min, max, step, \ @@ -282,8 +277,10 @@ static unsigned int mt6359_regulator_get_mode(struct regulator_dev *rdev) return ret; } - if ((regval & info->modeset_mask) >> info->modeset_shift == - MT6359_BUCK_MODE_FORCE_PWM) + regval &= info->modeset_mask; + regval >>= ffs(info->modeset_mask) - 1; + + if (regval == MT6359_BUCK_MODE_FORCE_PWM) return REGULATOR_MODE_FAST; ret = regmap_read(rdev->regmap, info->lp_mode_reg, ®val); @@ -310,7 +307,7 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_FAST: val = MT6359_BUCK_MODE_FORCE_PWM; - val <<= info->modeset_shift; + val <<= ffs(info->modeset_mask) - 1; ret = regmap_update_bits(rdev->regmap, info->modeset_reg, info->modeset_mask, @@ -319,14 +316,14 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev, case REGULATOR_MODE_NORMAL: if (curr_mode == REGULATOR_MODE_FAST) { val = MT6359_BUCK_MODE_AUTO; - val <<= info->modeset_shift; + val <<= ffs(info->modeset_mask) - 1; ret = regmap_update_bits(rdev->regmap, info->modeset_reg, info->modeset_mask, val); } else if (curr_mode == REGULATOR_MODE_IDLE) { val = MT6359_BUCK_MODE_NORMAL; - val <<= info->lp_mode_shift; + val <<= ffs(info->lp_mode_mask) - 1; ret = regmap_update_bits(rdev->regmap, info->lp_mode_reg, info->lp_mode_mask, @@ -336,7 +333,7 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev, break; case REGULATOR_MODE_IDLE: val = MT6359_BUCK_MODE_LP >> 1; - val <<= info->lp_mode_shift; + val <<= ffs(info->lp_mode_mask) - 1; ret = regmap_update_bits(rdev->regmap, info->lp_mode_reg, info->lp_mode_mask, From 12401a1cef787167aff52ef2dd28286e61054c38 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 29 Jun 2021 21:05:03 +0800 Subject: [PATCH 009/177] regulator: mt6397: Remove modeset_shift from struct mt6397_regulator_info The shift setting can be calculated via the corresponding mask field, so remove modeset_shift. Signed-off-by: Axel Lin Link: https://lore.kernel.org/r/20210629130503.2183574-3-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/mt6397-regulator.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/mt6397-regulator.c b/drivers/regulator/mt6397-regulator.c index 0a30df5e414f..b9bf7ade1f8a 100644 --- a/drivers/regulator/mt6397-regulator.c +++ b/drivers/regulator/mt6397-regulator.c @@ -32,7 +32,6 @@ struct mt6397_regulator_info { u32 vselctrl_mask; u32 modeset_reg; u32 modeset_mask; - u32 modeset_shift; }; #define MT6397_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \ @@ -61,7 +60,6 @@ struct mt6397_regulator_info { .vselctrl_mask = BIT(1), \ .modeset_reg = _modeset_reg, \ .modeset_mask = BIT(_modeset_shift), \ - .modeset_shift = _modeset_shift \ } #define MT6397_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \ @@ -175,11 +173,11 @@ static int mt6397_regulator_set_mode(struct regulator_dev *rdev, goto err_mode; } - dev_dbg(&rdev->dev, "mt6397 buck set_mode %#x, %#x, %#x, %#x\n", - info->modeset_reg, info->modeset_mask, - info->modeset_shift, val); + dev_dbg(&rdev->dev, "mt6397 buck set_mode %#x, %#x, %#x\n", + info->modeset_reg, info->modeset_mask, val); + + val <<= ffs(info->modeset_mask) - 1; - val <<= info->modeset_shift; ret = regmap_update_bits(rdev->regmap, info->modeset_reg, info->modeset_mask, val); err_mode: @@ -204,7 +202,10 @@ static unsigned int mt6397_regulator_get_mode(struct regulator_dev *rdev) return ret; } - switch ((regval & info->modeset_mask) >> info->modeset_shift) { + regval &= info->modeset_mask; + regval >>= ffs(info->modeset_mask) - 1; + + switch (regval) { case MT6397_BUCK_MODE_AUTO: return REGULATOR_MODE_NORMAL; case MT6397_BUCK_MODE_FORCE_PWM: From e301df76472cc929fa62d923bc3892931f7ad71d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 5 Jul 2021 23:12:11 +0300 Subject: [PATCH 010/177] regulator: tps65910: Silence deferred probe error The TPS65910 regulator now gets a deferred probe until supply regulator is registered. Silence noisy error message about the deferred probe. Reported-by: Matt Merhar # Ouya T30 Tested-by: Matt Merhar # Ouya T30 Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210705201211.16082-1-digetx@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 1d5b0a1b86f7..06cbe60c990f 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1211,12 +1211,10 @@ static int tps65910_probe(struct platform_device *pdev) rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i], &config); - if (IS_ERR(rdev)) { - dev_err(tps65910->dev, - "failed to register %s regulator\n", - pdev->name); - return PTR_ERR(rdev); - } + if (IS_ERR(rdev)) + return dev_err_probe(tps65910->dev, PTR_ERR(rdev), + "failed to register %s regulator\n", + pdev->name); /* Save regulator for cleanup */ pmic->rdev[i] = rdev; From 6eb891cf73bd2ecc877e9916951a19f3e4f3c493 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Tue, 6 Jul 2021 14:45:39 +0800 Subject: [PATCH 011/177] regulator: rt5033: Use linear ranges to map all voltage selection Instead of linear mapping, Use linear range to map all voltage selection. Signed-off-by: ChiYuan Huang Reviewed-by: Axel Lin Acked-by: Lee Jones Link: https://lore.kernel.org/r/1625553939-9109-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/rt5033-regulator.c | 21 +++++++++++++++------ include/linux/mfd/rt5033-private.h | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c index 0e7311629165..da4cf5a6acc2 100644 --- a/drivers/regulator/rt5033-regulator.c +++ b/drivers/regulator/rt5033-regulator.c @@ -13,6 +13,16 @@ #include #include +static const struct linear_range rt5033_buck_ranges[] = { + REGULATOR_LINEAR_RANGE(1000000, 0, 20, 100000), + REGULATOR_LINEAR_RANGE(3000000, 21, 31, 0), +}; + +static const struct linear_range rt5033_ldo_ranges[] = { + REGULATOR_LINEAR_RANGE(1200000, 0, 18, 100000), + REGULATOR_LINEAR_RANGE(3000000, 19, 31, 0), +}; + static const struct regulator_ops rt5033_safe_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, @@ -24,8 +34,7 @@ static const struct regulator_ops rt5033_buck_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .list_voltage = regulator_list_voltage_linear, - .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -40,8 +49,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .n_voltages = RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM, - .min_uV = RT5033_REGULATOR_BUCK_VOLTAGE_MIN, - .uV_step = RT5033_REGULATOR_BUCK_VOLTAGE_STEP, + .linear_ranges = rt5033_buck_ranges, + .n_linear_ranges = ARRAY_SIZE(rt5033_buck_ranges), .enable_reg = RT5033_REG_CTRL, .enable_mask = RT5033_CTRL_EN_BUCK_MASK, .vsel_reg = RT5033_REG_BUCK_CTRL, @@ -56,8 +65,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .n_voltages = RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM, - .min_uV = RT5033_REGULATOR_LDO_VOLTAGE_MIN, - .uV_step = RT5033_REGULATOR_LDO_VOLTAGE_STEP, + .linear_ranges = rt5033_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(rt5033_ldo_ranges), .enable_reg = RT5033_REG_CTRL, .enable_mask = RT5033_CTRL_EN_LDO_MASK, .vsel_reg = RT5033_REG_LDO_CTRL, diff --git a/include/linux/mfd/rt5033-private.h b/include/linux/mfd/rt5033-private.h index 40a0c2dfb80f..2d1895c3efbf 100644 --- a/include/linux/mfd/rt5033-private.h +++ b/include/linux/mfd/rt5033-private.h @@ -200,13 +200,13 @@ enum rt5033_reg { #define RT5033_REGULATOR_BUCK_VOLTAGE_MIN 1000000U #define RT5033_REGULATOR_BUCK_VOLTAGE_MAX 3000000U #define RT5033_REGULATOR_BUCK_VOLTAGE_STEP 100000U -#define RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM 21 +#define RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM 32 /* RT5033 regulator LDO output voltage uV */ #define RT5033_REGULATOR_LDO_VOLTAGE_MIN 1200000U #define RT5033_REGULATOR_LDO_VOLTAGE_MAX 3000000U #define RT5033_REGULATOR_LDO_VOLTAGE_STEP 100000U -#define RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM 19 +#define RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM 32 /* RT5033 regulator SAFE LDO output voltage uV */ #define RT5033_REGULATOR_SAFE_LDO_VOLTAGE 4900000U From 78bbb7c345ab630cfe8b272c6179bf8b19a6c8aa Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 27 Jun 2021 18:54:22 -0700 Subject: [PATCH 012/177] regulator: machine.h: fix kernel-doc "bad line" Fix warning caused by a blank/empty line: ../include/linux/regulator/machine.h:115: warning: bad line: Signed-off-by: Randy Dunlap Link: https://lore.kernel.org/r/20210628015422.8845-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/linux/regulator/machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 68b4a514a410..621b7f4a3639 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -112,7 +112,7 @@ struct notification_limit { * @over_voltage_limits: Limits for acting on over voltage. * @under_voltage_limits: Limits for acting on under voltage. * @temp_limits: Limits for acting on over temperature. - + * * @max_spread: Max possible spread between coupled regulators * @max_uV_step: Max possible step change in voltage * @valid_modes_mask: Mask of modes which may be configured by consumers. From 526e99cf43fe3884014f9e9f2ca5d04d56007287 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 9 Jul 2021 23:40:35 +0800 Subject: [PATCH 013/177] regulator: rtq6752: Add binding document for Richtek RTQ6752 Add binding document for Richtek RTQ6752. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1625845236-30285-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- .../regulator/richtek,rtq6752-regulator.yaml | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml b/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml new file mode 100644 index 000000000000..641840ea7c16 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/richtek,rtq6752-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RTQ6752 TFT LCD Voltage Regulator + +maintainers: + - ChiYuan Huang + +description: | + The RTQ6752 is an I2C interface pgorammable power management IC. It includes + two synchronous boost converter for PAVDD, and one synchronous NAVDD + buck-boost. The device is suitable for automotive TFT-LCD panel. + +properties: + compatible: + enum: + - richtek,rtq6752 + + reg: + maxItems: 1 + + enable-gpios: + description: | + A connection of the chip 'enable' gpio line. If not provided, treat it as + external pull up. + maxItems: 1 + + regulators: + type: object + $ref: regulator.yaml# + + patternProperties: + "^(p|n)avdd$": + type: object + $ref: regulator.yaml# + description: | + regulator description for pavdd and navdd. + + additionalProperties: false + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtq6752@6b { + compatible = "richtek,rtq6752"; + status = "okay"; + reg = <0x6b>; + enable-gpios = <&gpio26 2 0>; + + regulators { + pavdd { + regulator-name = "rtq6752-pavdd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <7300000>; + regulator-boot-on; + }; + navdd { + regulator-name = "rtq6752-navdd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <7300000>; + regulator-boot-on; + }; + }; + }; + }; From f40f9409719fa092924803723b9445be13fac8fb Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 9 Jul 2021 23:40:36 +0800 Subject: [PATCH 014/177] regulator: rt6752: Add support for Richtek RTQ6752 Add suport for Richtek RTQ6752. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1625845236-30285-2-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/rtq6752-regulator.c | 285 ++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 drivers/regulator/rtq6752-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 24ce9a17ab4f..6562d4c243b0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1066,6 +1066,15 @@ config REGULATOR_RTMV20 the Richtek RTMV20. It can support the load current up to 6A and integrate strobe/vsync/fsin signal to synchronize the IR camera. +config REGULATOR_RTQ6752 + tristate "Richtek RTQ6752 TFT LCD voltage regulator" + depends on I2C + select REGMAP_I2C + help + This driver adds support for Richtek RTQ6752. RTQ6752 includes two + synchronous boost converters for PAVDD, and one synchronous NAVDD + buck-boost. This device is suitable for automotive TFT-LCD panel. + config REGULATOR_S2MPA01 tristate "Samsung S2MPA01 voltage regulator" depends on MFD_SEC_CORE || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8c2f82206b94..102b2a0069a2 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o +obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o diff --git a/drivers/regulator/rtq6752-regulator.c b/drivers/regulator/rtq6752-regulator.c new file mode 100644 index 000000000000..fe31597983a6 --- /dev/null +++ b/drivers/regulator/rtq6752-regulator.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + RTQ6752_IDX_PAVDD = 0, + RTQ6752_IDX_NAVDD = 1, + RTQ6752_IDX_MAX +}; + +#define RTQ6752_REG_PAVDD 0x00 +#define RTQ6752_REG_NAVDD 0x01 +#define RTQ6752_REG_PAVDDONDLY 0x07 +#define RTQ6752_REG_PAVDDSSTIME 0x07 +#define RTQ6752_REG_NAVDDONDLY 0x0D +#define RTQ6752_REG_NAVDDSSTIME 0x0E +#define RTQ6752_REG_OPTION1 0x12 +#define RTQ6752_REG_CHSWITCH 0x16 +#define RTQ6752_REG_FAULT 0x1D + +#define RTQ6752_VOUT_MASK GENMASK(5, 0) +#define RTQ6752_NAVDDEN_MASK BIT(3) +#define RTQ6752_PAVDDEN_MASK BIT(0) +#define RTQ6752_PAVDDAD_MASK BIT(4) +#define RTQ6752_NAVDDAD_MASK BIT(3) +#define RTQ6752_PAVDDF_MASK BIT(3) +#define RTQ6752_NAVDDF_MASK BIT(0) +#define RTQ6752_ENABLE_MASK (BIT(RTQ6752_IDX_MAX) - 1) + +#define RTQ6752_VOUT_MINUV 5000000 +#define RTQ6752_VOUT_STEPUV 50000 +#define RTQ6752_VOUT_NUM 47 +#define RTQ6752_I2CRDY_TIMEUS 1000 +#define RTQ6752_MINSS_TIMEUS 5000 + +struct rtq6752_priv { + struct regmap *regmap; + struct gpio_desc *enable_gpio; + struct mutex lock; + unsigned char enable_flag; +}; + +static int rtq6752_set_vdd_enable(struct regulator_dev *rdev) +{ + struct rtq6752_priv *priv = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev), ret; + + mutex_lock(&priv->lock); + if (!priv->enable_flag && priv->enable_gpio) { + gpiod_set_value(priv->enable_gpio, 1); + + usleep_range(RTQ6752_I2CRDY_TIMEUS, + RTQ6752_I2CRDY_TIMEUS + 100); + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) { + mutex_unlock(&priv->lock); + return ret; + } + } + + priv->enable_flag |= BIT(rid); + mutex_unlock(&priv->lock); + + return regulator_enable_regmap(rdev); +} + +static int rtq6752_set_vdd_disable(struct regulator_dev *rdev) +{ + struct rtq6752_priv *priv = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev), ret; + + ret = regulator_disable_regmap(rdev); + if (ret) + return ret; + + mutex_lock(&priv->lock); + priv->enable_flag &= ~BIT(rid); + + if (!priv->enable_flag && priv->enable_gpio) { + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + + gpiod_set_value(priv->enable_gpio, 0); + } + mutex_unlock(&priv->lock); + + return 0; +} + +static int rtq6752_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + unsigned int val, events = 0; + const unsigned int fault_mask[] = { + RTQ6752_PAVDDF_MASK, RTQ6752_NAVDDF_MASK }; + int rid = rdev_get_id(rdev), ret; + + ret = regmap_read(rdev->regmap, RTQ6752_REG_FAULT, &val); + if (ret) + return ret; + + if (val & fault_mask[rid]) + events = REGULATOR_ERROR_REGULATION_OUT; + + *flags = events; + return 0; +} + +static const struct regulator_ops rtq6752_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = rtq6752_set_vdd_enable, + .disable = rtq6752_set_vdd_disable, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rtq6752_get_error_flags, +}; + +static const struct regulator_desc rtq6752_regulator_descs[] = { + { + .name = "rtq6752-pavdd", + .of_match = of_match_ptr("pavdd"), + .regulators_node = of_match_ptr("regulators"), + .id = RTQ6752_IDX_PAVDD, + .n_voltages = RTQ6752_VOUT_NUM, + .ops = &rtq6752_regulator_ops, + .owner = THIS_MODULE, + .min_uV = RTQ6752_VOUT_MINUV, + .uV_step = RTQ6752_VOUT_STEPUV, + .enable_time = RTQ6752_MINSS_TIMEUS, + .vsel_reg = RTQ6752_REG_PAVDD, + .vsel_mask = RTQ6752_VOUT_MASK, + .enable_reg = RTQ6752_REG_CHSWITCH, + .enable_mask = RTQ6752_PAVDDEN_MASK, + .active_discharge_reg = RTQ6752_REG_OPTION1, + .active_discharge_mask = RTQ6752_PAVDDAD_MASK, + .active_discharge_off = RTQ6752_PAVDDAD_MASK, + }, + { + .name = "rtq6752-navdd", + .of_match = of_match_ptr("navdd"), + .regulators_node = of_match_ptr("regulators"), + .id = RTQ6752_IDX_NAVDD, + .n_voltages = RTQ6752_VOUT_NUM, + .ops = &rtq6752_regulator_ops, + .owner = THIS_MODULE, + .min_uV = RTQ6752_VOUT_MINUV, + .uV_step = RTQ6752_VOUT_STEPUV, + .enable_time = RTQ6752_MINSS_TIMEUS, + .vsel_reg = RTQ6752_REG_NAVDD, + .vsel_mask = RTQ6752_VOUT_MASK, + .enable_reg = RTQ6752_REG_CHSWITCH, + .enable_mask = RTQ6752_NAVDDEN_MASK, + .active_discharge_reg = RTQ6752_REG_OPTION1, + .active_discharge_mask = RTQ6752_NAVDDAD_MASK, + .active_discharge_off = RTQ6752_NAVDDAD_MASK, + } +}; + +static int rtq6752_init_device_properties(struct rtq6752_priv *priv) +{ + u8 raw_vals[] = { 0, 0 }; + int ret; + + /* Configure PAVDD on and softstart delay time to the minimum */ + ret = regmap_raw_write(priv->regmap, RTQ6752_REG_PAVDDONDLY, raw_vals, + ARRAY_SIZE(raw_vals)); + if (ret) + return ret; + + /* Configure NAVDD on and softstart delay time to the minimum */ + return regmap_raw_write(priv->regmap, RTQ6752_REG_NAVDDONDLY, raw_vals, + ARRAY_SIZE(raw_vals)); +} + +static bool rtq6752_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == RTQ6752_REG_FAULT) + return true; + return false; +} + +static const struct reg_default rtq6752_reg_defaults[] = { + { RTQ6752_REG_PAVDD, 0x14 }, + { RTQ6752_REG_NAVDD, 0x14 }, + { RTQ6752_REG_PAVDDONDLY, 0x01 }, + { RTQ6752_REG_PAVDDSSTIME, 0x01 }, + { RTQ6752_REG_NAVDDONDLY, 0x01 }, + { RTQ6752_REG_NAVDDSSTIME, 0x01 }, + { RTQ6752_REG_OPTION1, 0x07 }, + { RTQ6752_REG_CHSWITCH, 0x29 }, +}; + +static const struct regmap_config rtq6752_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = RTQ6752_REG_FAULT, + .reg_defaults = rtq6752_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rtq6752_reg_defaults), + .volatile_reg = rtq6752_is_volatile_reg, +}; + +static int rtq6752_probe(struct i2c_client *i2c) +{ + struct rtq6752_priv *priv; + struct regulator_config reg_cfg = {}; + struct regulator_dev *rdev; + int i, ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + + priv->enable_gpio = devm_gpiod_get_optional(&i2c->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->enable_gpio)) { + dev_err(&i2c->dev, "Failed to get 'enable' gpio\n"); + return PTR_ERR(priv->enable_gpio); + } + + usleep_range(RTQ6752_I2CRDY_TIMEUS, RTQ6752_I2CRDY_TIMEUS + 100); + /* Default EN pin to high, PAVDD and NAVDD will be on */ + priv->enable_flag = RTQ6752_ENABLE_MASK; + + priv->regmap = devm_regmap_init_i2c(i2c, &rtq6752_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&i2c->dev, "Failed to init regmap\n"); + return PTR_ERR(priv->regmap); + } + + ret = rtq6752_init_device_properties(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to init device properties\n"); + return ret; + } + + reg_cfg.dev = &i2c->dev; + reg_cfg.regmap = priv->regmap; + reg_cfg.driver_data = priv; + + for (i = 0; i < ARRAY_SIZE(rtq6752_regulator_descs); i++) { + rdev = devm_regulator_register(&i2c->dev, + rtq6752_regulator_descs + i, + ®_cfg); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to init %d regulator\n", i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rtq6752_device_table[] = { + { .compatible = "richtek,rtq6752", }, + {} +}; +MODULE_DEVICE_TABLE(of, rtq6752_device_table); + +static struct i2c_driver rtq6752_driver = { + .driver = { + .name = "rtq6752", + .of_match_table = rtq6752_device_table, + }, + .probe_new = rtq6752_probe, +}; +module_i2c_driver(rtq6752_driver); + +MODULE_AUTHOR("ChiYuan Hwang "); +MODULE_DESCRIPTION("Richtek RTQ6752 Regulator Driver"); +MODULE_LICENSE("GPL v2"); From 894cda54a76d7c336b6f301bafe49bf6870d4697 Mon Sep 17 00:00:00 2001 From: Jinchao Wang Date: Sat, 26 Jun 2021 18:24:54 +0800 Subject: [PATCH 015/177] regulator: Replace symbolic permissions with octal permissions Resolve following checkpatch issue, Replace symbolic permissions with octal permissions Signed-off-by: Jinchao Wang Link: https://lore.kernel.org/r/20210626102454.54931-1-wjc@cdjrlc.com Signed-off-by: Mark Brown --- drivers/regulator/dbx500-prcmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 8b70bfe88019..a45c1e1ac7ef 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -117,11 +117,11 @@ ux500_regulator_debug_init(struct platform_device *pdev, rdebug.dir = debugfs_create_dir("ux500-regulator", NULL); /* create "status" file */ - debugfs_create_file("status", S_IRUGO, rdebug.dir, &pdev->dev, + debugfs_create_file("status", 0444, rdebug.dir, &pdev->dev, &ux500_regulator_status_fops); /* create "power-state-count" file */ - debugfs_create_file("power-state-count", S_IRUGO, rdebug.dir, + debugfs_create_file("power-state-count", 0444, rdebug.dir, &pdev->dev, &ux500_regulator_power_state_cnt_fops); rdebug.regulator_array = regulator_info; From fedbfea13cc0e513956abfa5c22158f0523d5687 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Mon, 5 Jul 2021 13:54:33 +0300 Subject: [PATCH 016/177] regulator: bd718x7: Suopport configuring UVP/OVP state The ROHM BD71837/47/50/78 do support enabling/disabling the under/over voltage protection. Add support for enabling/disabling the protection according to the device-tree information. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/20210705105416.GA1189560@localhost.localdomain Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 369 ++++++++++++++++++-------- 1 file changed, 260 insertions(+), 109 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index b1eb46961993..d60fccedb250 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -55,7 +55,8 @@ #define BD718XX_HWOPNAME(swopname) swopname##_hwcontrol #define BD718XX_OPS(name, _list_voltage, _map_voltage, _set_voltage_sel, \ - _get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay) \ + _get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay, \ + _set_uvp, _set_ovp) \ static const struct regulator_ops name = { \ .enable = regulator_enable_regmap, \ .disable = regulator_disable_regmap, \ @@ -66,6 +67,8 @@ static const struct regulator_ops name = { \ .get_voltage_sel = (_get_voltage_sel), \ .set_voltage_time_sel = (_set_voltage_time_sel), \ .set_ramp_delay = (_set_ramp_delay), \ + .set_under_voltage_protection = (_set_uvp), \ + .set_over_voltage_protection = (_set_ovp), \ }; \ \ static const struct regulator_ops BD718XX_HWOPNAME(name) = { \ @@ -76,6 +79,8 @@ static const struct regulator_ops BD718XX_HWOPNAME(name) = { \ .get_voltage_sel = (_get_voltage_sel), \ .set_voltage_time_sel = (_set_voltage_time_sel), \ .set_ramp_delay = (_set_ramp_delay), \ + .set_under_voltage_protection = (_set_uvp), \ + .set_over_voltage_protection = (_set_ovp), \ } \ /* @@ -154,17 +159,9 @@ static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel, * exceed it due to the scheduling. */ msleep(1); - /* - * Note for next hacker. The PWRGOOD should not be masked on - * BD71847 so we will just unconditionally enable detection - * when voltage is set. - * If someone want's to disable PWRGOOD he must implement - * caching and restoring the old value here. I am not - * aware of such use-cases so for the sake of the simplicity - * we just always enable PWRGOOD here. - */ - ret = regmap_update_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2, - *mask, 0); + + ret = regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2, + *mask); if (ret) dev_err(&rdev->dev, "Failed to re-enable voltage monitoring (%d)\n", @@ -208,12 +205,27 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel, * time configurable. */ if (new > now) { + int tmp; + int prot_bit; int ldo_offset = rdev->desc->id - BD718XX_LDO1; - *mask = BD718XX_LDO1_VRMON80 << ldo_offset; - ret = regmap_update_bits(rdev->regmap, - BD718XX_REG_MVRFLTMASK2, - *mask, *mask); + prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset; + ret = regmap_read(rdev->regmap, BD718XX_REG_MVRFLTMASK2, + &tmp); + if (ret) { + dev_err(&rdev->dev, + "Failed to read voltage monitoring state\n"); + return ret; + } + + if (!(tmp & prot_bit)) { + /* We disable protection if it was enabled... */ + ret = regmap_set_bits(rdev->regmap, + BD718XX_REG_MVRFLTMASK2, + prot_bit); + /* ...and we also want to re-enable it */ + *mask = prot_bit; + } if (ret) { dev_err(&rdev->dev, "Failed to stop voltage monitoring\n"); @@ -266,99 +278,6 @@ static int bd71837_set_voltage_sel_pickable_restricted( return regulator_set_voltage_sel_pickable_regmap(rdev, sel); } -/* - * OPS common for BD71847 and BD71850 - */ -BD718XX_OPS(bd718xx_pickable_range_ldo_ops, - regulator_list_voltage_pickable_linear_range, NULL, - bd718xx_set_voltage_sel_pickable_restricted, - regulator_get_voltage_sel_pickable_regmap, NULL, NULL); - -/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */ -static const struct regulator_ops bd718xx_ldo5_ops_hwstate = { - .is_enabled = never_enabled_by_hwstate, - .list_voltage = regulator_list_voltage_pickable_linear_range, - .set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted, - .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, -}; - -BD718XX_OPS(bd718xx_pickable_range_buck_ops, - regulator_list_voltage_pickable_linear_range, NULL, - regulator_set_voltage_sel_pickable_regmap, - regulator_get_voltage_sel_pickable_regmap, - regulator_set_voltage_time_sel, NULL); - -BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range, - NULL, bd718xx_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, NULL, NULL); - -BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table, - NULL, bd718xx_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, NULL, NULL); - -BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range, - NULL, regulator_set_voltage_sel_regmap, - regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - NULL); - -BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table, - regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap, - regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - NULL); - -/* - * OPS for BD71837 - */ -BD718XX_OPS(bd71837_pickable_range_ldo_ops, - regulator_list_voltage_pickable_linear_range, NULL, - bd71837_set_voltage_sel_pickable_restricted, - regulator_get_voltage_sel_pickable_regmap, NULL, NULL); - -BD718XX_OPS(bd71837_pickable_range_buck_ops, - regulator_list_voltage_pickable_linear_range, NULL, - bd71837_set_voltage_sel_pickable_restricted, - regulator_get_voltage_sel_pickable_regmap, - regulator_set_voltage_time_sel, NULL); - -BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range, - NULL, bd71837_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, NULL, NULL); - -BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table, - NULL, bd71837_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, NULL, NULL); - -BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range, - NULL, bd71837_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - NULL); - -BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table, - regulator_map_voltage_ascend, bd71837_set_voltage_sel_restricted, - regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - NULL); -/* - * BD71837 bucks 3 and 4 support defining their enable/disable state also - * when buck enable state is under HW state machine control. In that case the - * bit [2] in CTRL register is used to indicate if regulator should be ON. - */ -static const struct regulator_ops bd71837_buck34_ops_hwctrl = { - .is_enabled = bd71837_get_buck34_enable_hwctrl, - .list_voltage = regulator_list_voltage_linear_range, - .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_ramp_delay = regulator_set_ramp_delay_regmap, -}; - -/* - * OPS for all of the ICs - BD718(37/47/50) - */ -BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range, - NULL, regulator_set_voltage_sel_regmap, - regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - /* bd718xx_buck1234_set_ramp_delay */ regulator_set_ramp_delay_regmap); - /* * BD71837 BUCK1/2/3/4 * BD71847 BUCK1/2 @@ -536,6 +455,238 @@ struct bd718xx_regulator_data { int additional_init_amnt; }; +static int bd718x7_xvp_sanity_check(struct regulator_dev *rdev, int lim_uV, + int severity) +{ + /* + * BD71837/47/50 ... (ICs supported by this driver) do not provide + * warnings, only protection + */ + if (severity != REGULATOR_SEVERITY_PROT) { + dev_err(&rdev->dev, + "Unsupported Under Voltage protection level\n"); + return -EINVAL; + } + + /* + * And protection limit is not changeable. It can only be enabled + * or disabled + */ + if (lim_uV) + return -EINVAL; + + return 0; +} + +static int bd718x7_set_ldo_uvp(struct regulator_dev *rdev, int lim_uV, + int severity, bool enable) +{ + int ldo_offset = rdev->desc->id - BD718XX_LDO1; + int prot_bit, ret; + + ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); + if (ret) + return ret; + + prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset; + + if (enable) + return regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2, + prot_bit); + + return regmap_set_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2, + prot_bit); +} + +static int bd718x7_get_buck_prot_reg(int id, int *reg) +{ + + if (id > BD718XX_BUCK8) { + WARN_ON(id > BD718XX_BUCK8); + return -EINVAL; + } + + if (id > BD718XX_BUCK4) + *reg = BD718XX_REG_MVRFLTMASK0; + else + *reg = BD718XX_REG_MVRFLTMASK1; + + return 0; +} + +static int bd718x7_get_buck_ovp_info(int id, int *reg, int *bit) +{ + int ret; + + ret = bd718x7_get_buck_prot_reg(id, reg); + if (ret) + return ret; + + *bit = BIT((id % 4) * 2 + 1); + + return 0; +} + +static int bd718x7_get_buck_uvp_info(int id, int *reg, int *bit) +{ + int ret; + + ret = bd718x7_get_buck_prot_reg(id, reg); + if (ret) + return ret; + + *bit = BIT((id % 4) * 2); + + return 0; +} + +static int bd718x7_set_buck_uvp(struct regulator_dev *rdev, int lim_uV, + int severity, bool enable) +{ + int bit, reg, ret; + + ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); + if (ret) + return ret; + + ret = bd718x7_get_buck_uvp_info(rdev->desc->id, ®, &bit); + if (ret) + return ret; + + if (enable) + return regmap_clear_bits(rdev->regmap, reg, bit); + + return regmap_set_bits(rdev->regmap, reg, bit); + +} + +static int bd718x7_set_buck_ovp(struct regulator_dev *rdev, int lim_uV, + int severity, + bool enable) +{ + int bit, reg, ret; + + ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); + if (ret) + return ret; + + ret = bd718x7_get_buck_ovp_info(rdev->desc->id, ®, &bit); + if (ret) + return ret; + + if (enable) + return regmap_clear_bits(rdev->regmap, reg, bit); + + return regmap_set_bits(rdev->regmap, reg, bit); +} + +/* + * OPS common for BD71847 and BD71850 + */ +BD718XX_OPS(bd718xx_pickable_range_ldo_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd718xx_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, NULL, NULL, + bd718x7_set_ldo_uvp, NULL); + +/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */ +static const struct regulator_ops bd718xx_ldo5_ops_hwstate = { + .is_enabled = never_enabled_by_hwstate, + .list_voltage = regulator_list_voltage_pickable_linear_range, + .set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted, + .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, + .set_under_voltage_protection = bd718x7_set_ldo_uvp, +}; + +BD718XX_OPS(bd718xx_pickable_range_buck_ops, + regulator_list_voltage_pickable_linear_range, NULL, + regulator_set_voltage_sel_pickable_regmap, + regulator_get_voltage_sel_pickable_regmap, + regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp, + bd718x7_set_buck_ovp); + +BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd718xx_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, + NULL); + +BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table, + NULL, bd718xx_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, + NULL); + +BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); + +BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table, + regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); + +/* + * OPS for BD71837 + */ +BD718XX_OPS(bd71837_pickable_range_ldo_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd71837_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, NULL, NULL, + bd718x7_set_ldo_uvp, NULL); + +BD718XX_OPS(bd71837_pickable_range_buck_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd71837_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, + regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp, + bd718x7_set_buck_ovp); + +BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, + NULL); + +BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, + NULL); + +BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); + +BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table, + regulator_map_voltage_ascend, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); +/* + * BD71837 bucks 3 and 4 support defining their enable/disable state also + * when buck enable state is under HW state machine control. In that case the + * bit [2] in CTRL register is used to indicate if regulator should be ON. + */ +static const struct regulator_ops bd71837_buck34_ops_hwctrl = { + .is_enabled = bd71837_get_buck34_enable_hwctrl, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = regulator_set_ramp_delay_regmap, + .set_under_voltage_protection = bd718x7_set_buck_uvp, + .set_over_voltage_protection = bd718x7_set_buck_ovp, +}; + +/* + * OPS for all of the ICs - BD718(37/47/50) + */ +BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + regulator_set_ramp_delay_regmap, bd718x7_set_buck_uvp, + bd718x7_set_buck_ovp); + + + /* * There is a HW quirk in BD71837. The shutdown sequence timings for * bucks/LDOs which are controlled via register interface are changed. From 9d5354145104cf392568a948c5ce2cb97f373fd7 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 7 Jul 2021 10:27:01 +0200 Subject: [PATCH 017/177] spi: stm32: enable pm_runtime autosuspend This commit enables the pm_runtime autosuspend and sets a 1ms autosuspend delay. Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1625646426-5826-3-git-send-email-alain.volmat@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 05618a618939..4dbd5cbe0c11 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -162,6 +162,8 @@ #define SPI_3WIRE_TX 3 #define SPI_3WIRE_RX 4 +#define STM32_SPI_AUTOSUSPEND_DELAY 1 /* 1 ms */ + /* * use PIO for small transfers, avoiding DMA setup/teardown overhead for drivers * without fifo buffers. @@ -1927,6 +1929,9 @@ static int stm32_spi_probe(struct platform_device *pdev) if (spi->dma_tx || spi->dma_rx) master->can_dma = stm32_spi_can_dma; + pm_runtime_set_autosuspend_delay(&pdev->dev, + STM32_SPI_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -1938,6 +1943,9 @@ static int stm32_spi_probe(struct platform_device *pdev) goto err_pm_disable; } + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + dev_info(&pdev->dev, "driver initialized\n"); return 0; @@ -1946,6 +1954,7 @@ err_pm_disable: pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); err_dma_release: if (spi->dma_tx) dma_release_channel(spi->dma_tx); @@ -1970,6 +1979,8 @@ static int stm32_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + if (master->dma_tx) dma_release_channel(master->dma_tx); if (master->dma_rx) From 70526e0b7601792bf546044fff92c368112f1d3f Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 7 Jul 2021 10:27:03 +0200 Subject: [PATCH 018/177] spi: stm32: Revert "properly handle 0 byte transfer" 0 byte transfer handling is now done within the core in code added by commit b306320322c9 ("spi: Skip zero-length transfers in spi_transfer_one_message()") This reverts commit 2269f5a8b1a7 ("spi: stm32: properly handle 0 byte transfer") Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1625646426-5826-5-git-send-email-alain.volmat@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 4dbd5cbe0c11..d37bfead4d8c 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1647,10 +1647,6 @@ static int stm32_spi_transfer_one(struct spi_master *master, struct stm32_spi *spi = spi_master_get_devdata(master); int ret; - /* Don't do anything on 0 bytes transfers */ - if (transfer->len == 0) - return 0; - spi->tx_buf = transfer->tx_buf; spi->rx_buf = transfer->rx_buf; spi->tx_len = spi->tx_buf ? transfer->len : 0; From d87a5d64b5037cfedd7eb47d785b5c159ace8d9b Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Wed, 7 Jul 2021 10:27:04 +0200 Subject: [PATCH 019/177] spi: stm32h7: rework rx fifo read function Remove flush parameter and check RXWNE or RXPLVL when end of transfer flag is set. Signed-off-by: Amelie Delaunay Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1625646426-5826-6-git-send-email-alain.volmat@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index d37bfead4d8c..c2144e3c57eb 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -570,29 +570,30 @@ static void stm32f4_spi_read_rx(struct stm32_spi *spi) /** * stm32h7_spi_read_rxfifo - Read bytes in Receive Data Register * @spi: pointer to the spi controller data structure - * @flush: boolean indicating that FIFO should be flushed * * Write in rx_buf depends on remaining bytes to avoid to write beyond * rx_buf end. */ -static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush) +static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi) { u32 sr = readl_relaxed(spi->base + STM32H7_SPI_SR); u32 rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr); while ((spi->rx_len > 0) && ((sr & STM32H7_SPI_SR_RXP) || - (flush && ((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) { + ((sr & STM32H7_SPI_SR_EOT) && + ((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) { u32 offs = spi->cur_xferlen - spi->rx_len; if ((spi->rx_len >= sizeof(u32)) || - (flush && (sr & STM32H7_SPI_SR_RXWNE))) { + (sr & STM32H7_SPI_SR_RXWNE)) { u32 *rx_buf32 = (u32 *)(spi->rx_buf + offs); *rx_buf32 = readl_relaxed(spi->base + STM32H7_SPI_RXDR); spi->rx_len -= sizeof(u32); } else if ((spi->rx_len >= sizeof(u16)) || - (flush && (rxplvl >= 2 || spi->cur_bpw > 8))) { + (!(sr & STM32H7_SPI_SR_RXWNE) && + (rxplvl >= 2 || spi->cur_bpw > 8))) { u16 *rx_buf16 = (u16 *)(spi->rx_buf + offs); *rx_buf16 = readw_relaxed(spi->base + STM32H7_SPI_RXDR); @@ -608,8 +609,8 @@ static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush) rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr); } - dev_dbg(spi->dev, "%s%s: %d bytes left\n", __func__, - flush ? "(flush)" : "", spi->rx_len); + dev_dbg(spi->dev, "%s: %d bytes left (sr=%08x)\n", + __func__, spi->rx_len, sr); } /** @@ -677,12 +678,7 @@ static void stm32f4_spi_disable(struct stm32_spi *spi) * @spi: pointer to the spi controller data structure * * RX-Fifo is flushed when SPI controller is disabled. To prevent any data - * loss, use stm32h7_spi_read_rxfifo(flush) to read the remaining bytes in - * RX-Fifo. - * Normally, if TSIZE has been configured, we should relax the hardware at the - * reception of the EOT interrupt. But in case of error, EOT will not be - * raised. So the subsystem unprepare_message call allows us to properly - * complete the transfer from an hardware point of view. + * loss, use stm32_spi_read_rxfifo to read the remaining bytes in RX-Fifo. */ static void stm32h7_spi_disable(struct stm32_spi *spi) { @@ -717,7 +713,7 @@ static void stm32h7_spi_disable(struct stm32_spi *spi) } if (!spi->cur_usedma && spi->rx_buf && (spi->rx_len > 0)) - stm32h7_spi_read_rxfifo(spi, true); + stm32h7_spi_read_rxfifo(spi); if (spi->cur_usedma && spi->dma_tx) dmaengine_terminate_all(spi->dma_tx); @@ -913,7 +909,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (__ratelimit(&rs)) dev_dbg_ratelimited(spi->dev, "Communication suspended\n"); if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) - stm32h7_spi_read_rxfifo(spi, false); + stm32h7_spi_read_rxfifo(spi); /* * If communication is suspended while using DMA, it means * that something went wrong, so stop the current transfer @@ -934,7 +930,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (sr & STM32H7_SPI_SR_EOT) { if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) - stm32h7_spi_read_rxfifo(spi, true); + stm32h7_spi_read_rxfifo(spi); end = true; } @@ -944,7 +940,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (sr & STM32H7_SPI_SR_RXP) if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) - stm32h7_spi_read_rxfifo(spi, false); + stm32h7_spi_read_rxfifo(spi); writel_relaxed(sr & mask, spi->base + STM32H7_SPI_IFCR); From dc6620c31326bc50fa22fd8900a9f995d0a04bc1 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 7 Jul 2021 10:27:05 +0200 Subject: [PATCH 020/177] spi: stm32h7: don't wait for EOT and flush fifo on disable In nominal cases, disable is called as part of the unprepare_message, after receiving a EOT and after receiving all data so it doesn't make sense to check for EOT and empty the FIFO. Moreover, at the end of the disable, the SPI is disable (SPE) leading to clear of all internal FIFO, leaving the IP in a known status. Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1625646426-5826-7-git-send-email-alain.volmat@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index c2144e3c57eb..535f4bebc010 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -677,13 +677,12 @@ static void stm32f4_spi_disable(struct stm32_spi *spi) * stm32h7_spi_disable - Disable SPI controller * @spi: pointer to the spi controller data structure * - * RX-Fifo is flushed when SPI controller is disabled. To prevent any data - * loss, use stm32_spi_read_rxfifo to read the remaining bytes in RX-Fifo. + * RX-Fifo is flushed when SPI controller is disabled. */ static void stm32h7_spi_disable(struct stm32_spi *spi) { unsigned long flags; - u32 cr1, sr; + u32 cr1; dev_dbg(spi->dev, "disable controller\n"); @@ -696,25 +695,6 @@ static void stm32h7_spi_disable(struct stm32_spi *spi) return; } - /* Wait on EOT or suspend the flow */ - if (readl_relaxed_poll_timeout_atomic(spi->base + STM32H7_SPI_SR, - sr, !(sr & STM32H7_SPI_SR_EOT), - 10, 100000) < 0) { - if (cr1 & STM32H7_SPI_CR1_CSTART) { - writel_relaxed(cr1 | STM32H7_SPI_CR1_CSUSP, - spi->base + STM32H7_SPI_CR1); - if (readl_relaxed_poll_timeout_atomic( - spi->base + STM32H7_SPI_SR, - sr, !(sr & STM32H7_SPI_SR_SUSP), - 10, 100000) < 0) - dev_warn(spi->dev, - "Suspend request timeout\n"); - } - } - - if (!spi->cur_usedma && spi->rx_buf && (spi->rx_len > 0)) - stm32h7_spi_read_rxfifo(spi); - if (spi->cur_usedma && spi->dma_tx) dmaengine_terminate_all(spi->dma_tx); if (spi->cur_usedma && spi->dma_rx) From 7ceb0b8a3ceddc36ae4ef1cba6c25a0e28ed65fc Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 7 Jul 2021 10:27:06 +0200 Subject: [PATCH 021/177] spi: stm32: finalize message either on dma callback or EOT Depending on the usage, it is necessary to perform the finalize message operation either upon receiving the EOT interruption, eiher upon receiving the DMA callback. Indeed, when relying on DMA, even if the SPI EOT IT has been received, it is necessary to wait for the end of the DMA RX transaction before accessing to the data. Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1625646426-5826-8-git-send-email-alain.volmat@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 57 +++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 535f4bebc010..14ca7ea04e47 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -911,7 +911,10 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (sr & STM32H7_SPI_SR_EOT) { if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi); - end = true; + if (!spi->cur_usedma || + (spi->cur_usedma && (spi->cur_comm == SPI_SIMPLEX_TX || + spi->cur_comm == SPI_3WIRE_TX))) + end = true; } if (sr & STM32H7_SPI_SR_TXP) @@ -1019,42 +1022,17 @@ static void stm32f4_spi_dma_tx_cb(void *data) } /** - * stm32f4_spi_dma_rx_cb - dma callback + * stm32_spi_dma_rx_cb - dma callback * @data: pointer to the spi controller data structure * * DMA callback is called when the transfer is complete for DMA RX channel. */ -static void stm32f4_spi_dma_rx_cb(void *data) +static void stm32_spi_dma_rx_cb(void *data) { struct stm32_spi *spi = data; spi_finalize_current_transfer(spi->master); - stm32f4_spi_disable(spi); -} - -/** - * stm32h7_spi_dma_cb - dma callback - * @data: pointer to the spi controller data structure - * - * DMA callback is called when the transfer is complete or when an error - * occurs. If the transfer is complete, EOT flag is raised. - */ -static void stm32h7_spi_dma_cb(void *data) -{ - struct stm32_spi *spi = data; - unsigned long flags; - u32 sr; - - spin_lock_irqsave(&spi->lock, flags); - - sr = readl_relaxed(spi->base + STM32H7_SPI_SR); - - spin_unlock_irqrestore(&spi->lock, flags); - - if (!(sr & STM32H7_SPI_SR_EOT)) - dev_warn(spi->dev, "DMA error (sr=0x%08x)\n", sr); - - /* Now wait for EOT, or SUSP or OVR in case of error */ + spi->cfg->disable(spi); } /** @@ -1220,11 +1198,13 @@ static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi) */ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) { - /* Enable the interrupts relative to the end of transfer */ - stm32_spi_set_bits(spi, STM32H7_SPI_IER, STM32H7_SPI_IER_EOTIE | - STM32H7_SPI_IER_TXTFIE | - STM32H7_SPI_IER_OVRIE | - STM32H7_SPI_IER_MODFIE); + uint32_t ier = STM32H7_SPI_IER_OVRIE | STM32H7_SPI_IER_MODFIE; + + /* Enable the interrupts */ + if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) + ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE; + + stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier); stm32_spi_enable(spi); @@ -1736,7 +1716,7 @@ static const struct stm32_spi_cfg stm32f4_spi_cfg = { .set_mode = stm32f4_spi_set_mode, .transfer_one_dma_start = stm32f4_spi_transfer_one_dma_start, .dma_tx_cb = stm32f4_spi_dma_tx_cb, - .dma_rx_cb = stm32f4_spi_dma_rx_cb, + .dma_rx_cb = stm32_spi_dma_rx_cb, .transfer_one_irq = stm32f4_spi_transfer_one_irq, .irq_handler_event = stm32f4_spi_irq_event, .irq_handler_thread = stm32f4_spi_irq_thread, @@ -1756,8 +1736,11 @@ static const struct stm32_spi_cfg stm32h7_spi_cfg = { .set_data_idleness = stm32h7_spi_data_idleness, .set_number_of_data = stm32h7_spi_number_of_data, .transfer_one_dma_start = stm32h7_spi_transfer_one_dma_start, - .dma_rx_cb = stm32h7_spi_dma_cb, - .dma_tx_cb = stm32h7_spi_dma_cb, + .dma_rx_cb = stm32_spi_dma_rx_cb, + /* + * dma_tx_cb is not necessary since in case of TX, dma is followed by + * SPI access hence handling is performed within the SPI interrupt + */ .transfer_one_irq = stm32h7_spi_transfer_one_irq, .irq_handler_thread = stm32h7_spi_irq_thread, .baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN, From 8dd591ad0104593f315b6b2ab636a18c002f7d86 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 28 Jun 2021 14:05:20 -0700 Subject: [PATCH 022/177] spi: : add missing struct kernel-doc entry Fix kernel-doc warning in spi.h by adding the missing kernel-doc entry and also correct the original comment so that they both indicate the correct polarity of the flag. ../include/linux/spi/spi.h:673: warning: Function parameter or member 'devm_allocated' not described in 'spi_controller' Fixes: 794aaf01444d ("spi: Fix use-after-free with devm_spi_alloc_*") Signed-off-by: Randy Dunlap Cc: William A. Kennington III Cc: Mark Brown Cc: linux-spi@vger.kernel.org Cc: Lukas Wunner Reviewed-by: Lukas Wunner Link: https://lore.kernel.org/r/20210628210520.5712-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 97b8d12b5f2b..3a81b5d1c3cb 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -339,6 +339,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver * @slave: indicates that this is an SPI slave controller + * @devm_allocated: whether the allocation of this struct is devres-managed * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. * @max_message_size: function that returns the max message size for @@ -511,7 +512,7 @@ struct spi_controller { #define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */ - /* flag indicating this is a non-devres managed controller */ + /* flag indicating if the allocation of this struct is devres-managed */ bool devm_allocated; /* flag indicating this is an SPI slave controller */ From 3522d9aa19285bbff14da20cb3481e36ef4835fd Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Tue, 29 Jun 2021 18:13:11 +0800 Subject: [PATCH 023/177] spi: mediatek: update spi master bingdings for MT6893 SOC this patch update spi master bingdings for MT6893 SOC. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210629101310.21045-1-mason.zhang@mediatek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mt65xx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt index 4d0e4c15c4ea..2a24969159cc 100644 --- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt @@ -11,6 +11,7 @@ Required properties: - mediatek,mt8135-spi: for mt8135 platforms - mediatek,mt8173-spi: for mt8173 platforms - mediatek,mt8183-spi: for mt8183 platforms + - mediatek,mt6893-spi: for mt6893 platforms - "mediatek,mt8192-spi", "mediatek,mt6765-spi": for mt8192 platforms - "mediatek,mt8195-spi", "mediatek,mt6765-spi": for mt8195 platforms - "mediatek,mt8516-spi", "mediatek,mt2712-spi": for mt8516 platforms From 162a31effc4182dd5a0675d9fd0336d5096e0ad3 Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Tue, 29 Jun 2021 18:08:15 +0800 Subject: [PATCH 024/177] spi: mediatek: add no_need_unprepare support This patch add no_need_unprepare support for spi, if spi src clk is MAIN PLL, it can keep the clk_prepare and will not cause low power issue. So we no need do clk_prepare/clk_unprepare in runtime pm, and it will get better performance, because clk_prepare has called mutex lock. In the same way, clk_get_rate also has called mutex lock, so we moved it to spi_probe. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210629100814.21402-1-mason.zhang@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 41 +++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 976f73b9e299..097625d7915e 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -90,6 +90,8 @@ struct mtk_spi_compatible { bool enhance_timing; /* some IC support DMA addr extension */ bool dma_ext; + /* some IC no need unprepare SPI clk */ + bool no_need_unprepare; }; struct mtk_spi { @@ -104,6 +106,7 @@ struct mtk_spi { struct scatterlist *tx_sgl, *rx_sgl; u32 tx_sgl_len, rx_sgl_len; const struct mtk_spi_compatible *dev_comp; + u32 spi_clk_hz; }; static const struct mtk_spi_compatible mtk_common_compat; @@ -135,6 +138,14 @@ static const struct mtk_spi_compatible mt8183_compat = { .enhance_timing = true, }; +static const struct mtk_spi_compatible mt6893_compat = { + .need_pad_sel = true, + .must_tx = true, + .enhance_timing = true, + .dma_ext = true, + .no_need_unprepare = true, +}; + /* * A piece of default chip info unless the platform * supplies it. @@ -174,6 +185,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt8192-spi", .data = (void *)&mt6765_compat, }, + { .compatible = "mediatek,mt6893-spi", + .data = (void *)&mt6893_compat, + }, {} }; MODULE_DEVICE_TABLE(of, mtk_spi_of_match); @@ -287,12 +301,11 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) static void mtk_spi_prepare_transfer(struct spi_master *master, struct spi_transfer *xfer) { - u32 spi_clk_hz, div, sck_time, reg_val; + u32 div, sck_time, reg_val; struct mtk_spi *mdata = spi_master_get_devdata(master); - spi_clk_hz = clk_get_rate(mdata->spi_clk); - if (xfer->speed_hz < spi_clk_hz / 2) - div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz); + if (xfer->speed_hz < mdata->spi_clk_hz / 2) + div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz); else div = 1; @@ -789,7 +802,12 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_put_master; } - clk_disable_unprepare(mdata->spi_clk); + mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); + + if (mdata->dev_comp->no_need_unprepare) + clk_disable(mdata->spi_clk); + else + clk_disable_unprepare(mdata->spi_clk); pm_runtime_enable(&pdev->dev); @@ -857,6 +875,9 @@ static int mtk_spi_remove(struct platform_device *pdev) mtk_spi_reset(mdata); + if (mdata->dev_comp->no_need_unprepare) + clk_unprepare(mdata->spi_clk); + return 0; } @@ -905,7 +926,10 @@ static int mtk_spi_runtime_suspend(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct mtk_spi *mdata = spi_master_get_devdata(master); - clk_disable_unprepare(mdata->spi_clk); + if (mdata->dev_comp->no_need_unprepare) + clk_disable(mdata->spi_clk); + else + clk_disable_unprepare(mdata->spi_clk); return 0; } @@ -916,7 +940,10 @@ static int mtk_spi_runtime_resume(struct device *dev) struct mtk_spi *mdata = spi_master_get_devdata(master); int ret; - ret = clk_prepare_enable(mdata->spi_clk); + if (mdata->dev_comp->no_need_unprepare) + ret = clk_enable(mdata->spi_clk); + else + ret = clk_prepare_enable(mdata->spi_clk); if (ret < 0) { dev_err(dev, "failed to enable spi_clk (%d)\n", ret); return ret; From 9608703e488cf7a711c42c7ccd981c32377f7b78 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 12 Apr 2021 15:50:21 +0200 Subject: [PATCH 025/177] mm: Fix comments mentioning i_mutex inode->i_mutex has been replaced with inode->i_rwsem long ago. Fix comments still mentioning i_mutex. Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Acked-by: Hugh Dickins Signed-off-by: Jan Kara --- mm/filemap.c | 10 +++++----- mm/madvise.c | 2 +- mm/memory-failure.c | 2 +- mm/rmap.c | 6 +++--- mm/shmem.c | 20 ++++++++++---------- mm/truncate.c | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index d1458ecf2f51..acf20eca2fa4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -76,7 +76,7 @@ * ->swap_lock (exclusive_swap_page, others) * ->i_pages lock * - * ->i_mutex + * ->i_rwsem * ->i_mmap_rwsem (truncate->unmap_mapping_range) * * ->mmap_lock @@ -87,7 +87,7 @@ * ->mmap_lock * ->lock_page (access_process_vm) * - * ->i_mutex (generic_perform_write) + * ->i_rwsem (generic_perform_write) * ->mmap_lock (fault_in_pages_readable->do_page_fault) * * bdi->wb.list_lock @@ -3704,12 +3704,12 @@ EXPORT_SYMBOL(generic_perform_write); * modification times and calls proper subroutines depending on whether we * do direct IO or a standard buffered write. * - * It expects i_mutex to be grabbed unless we work on a block device or similar + * It expects i_rwsem to be grabbed unless we work on a block device or similar * object which does not need locking at all. * * This function does *not* take care of syncing data in case of O_SYNC write. * A caller has to handle it. This is mainly due to the fact that we want to - * avoid syncing under i_mutex. + * avoid syncing under i_rwsem. * * Return: * * number of bytes written, even for truncated writes @@ -3797,7 +3797,7 @@ EXPORT_SYMBOL(__generic_file_write_iter); * * This is a wrapper around __generic_file_write_iter() to be used by most * filesystems. It takes care of syncing the file in case of O_SYNC file - * and acquires i_mutex as needed. + * and acquires i_rwsem as needed. * Return: * * negative error code if no data has been written at all of * vfs_fsync_range() failed for a synchronous write diff --git a/mm/madvise.c b/mm/madvise.c index 6d3d348b17f4..012129fbfaf8 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -910,7 +910,7 @@ static long madvise_remove(struct vm_area_struct *vma, + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); /* - * Filesystem's fallocate may need to take i_mutex. We need to + * Filesystem's fallocate may need to take i_rwsem. We need to * explicitly grab a reference because the vma (and hence the * vma's reference to the file) can go away as soon as we drop * mmap_lock. diff --git a/mm/memory-failure.c b/mm/memory-failure.c index eefd823deb67..0edce65731f7 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -866,7 +866,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) /* * Truncation is a bit tricky. Enable it per file system for now. * - * Open: to take i_mutex or not for this? Right now we don't. + * Open: to take i_rwsem or not for this? Right now we don't. */ ret = truncate_error_page(p, pfn, mapping); out: diff --git a/mm/rmap.c b/mm/rmap.c index 795f9d5f8386..a8b01929ab2e 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -20,9 +20,9 @@ /* * Lock ordering in mm: * - * inode->i_mutex (while writing or truncating, not reading or faulting) + * inode->i_rwsem (while writing or truncating, not reading or faulting) * mm->mmap_lock - * page->flags PG_locked (lock_page) * (see huegtlbfs below) + * page->flags PG_locked (lock_page) * (see hugetlbfs below) * hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share) * mapping->i_mmap_rwsem * hugetlb_fault_mutex (hugetlbfs specific page fault mutex) @@ -41,7 +41,7 @@ * in arch-dependent flush_dcache_mmap_lock, * within bdi.wb->list_lock in __sync_single_inode) * - * anon_vma->rwsem,mapping->i_mutex (memory_failure, collect_procs_anon) + * anon_vma->rwsem,mapping->i_mmap_rwsem (memory_failure, collect_procs_anon) * ->tasklist_lock * pte map lock * diff --git a/mm/shmem.c b/mm/shmem.c index 70d9ce294bb4..9af4b2173fe9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -96,7 +96,7 @@ static struct vfsmount *shm_mnt; /* * shmem_fallocate communicates with shmem_fault or shmem_writepage via - * inode->i_private (with i_mutex making sure that it has only one user at + * inode->i_private (with i_rwsem making sure that it has only one user at * a time): we would prefer not to enlarge the shmem inode just for that. */ struct shmem_falloc { @@ -774,7 +774,7 @@ static int shmem_free_swap(struct address_space *mapping, * Determine (in bytes) how many of the shmem object's pages mapped by the * given offsets are swapped out. * - * This is safe to call without i_mutex or the i_pages lock thanks to RCU, + * This is safe to call without i_rwsem or the i_pages lock thanks to RCU, * as long as the inode doesn't go away and racy results are not a problem. */ unsigned long shmem_partial_swap_usage(struct address_space *mapping, @@ -806,7 +806,7 @@ unsigned long shmem_partial_swap_usage(struct address_space *mapping, * Determine (in bytes) how many of the shmem object's pages mapped by the * given vma is swapped out. * - * This is safe to call without i_mutex or the i_pages lock thanks to RCU, + * This is safe to call without i_rwsem or the i_pages lock thanks to RCU, * as long as the inode doesn't go away and racy results are not a problem. */ unsigned long shmem_swap_usage(struct vm_area_struct *vma) @@ -1069,7 +1069,7 @@ static int shmem_setattr(struct user_namespace *mnt_userns, loff_t oldsize = inode->i_size; loff_t newsize = attr->ia_size; - /* protected by i_mutex */ + /* protected by i_rwsem */ if ((newsize < oldsize && (info->seals & F_SEAL_SHRINK)) || (newsize > oldsize && (info->seals & F_SEAL_GROW))) return -EPERM; @@ -2071,7 +2071,7 @@ static vm_fault_t shmem_fault(struct vm_fault *vmf) /* * Trinity finds that probing a hole which tmpfs is punching can * prevent the hole-punch from ever completing: which in turn - * locks writers out with its hold on i_mutex. So refrain from + * locks writers out with its hold on i_rwsem. So refrain from * faulting pages into the hole while it's being punched. Although * shmem_undo_range() does remove the additions, it may be unable to * keep up, as each new page needs its own unmap_mapping_range() call, @@ -2082,7 +2082,7 @@ static vm_fault_t shmem_fault(struct vm_fault *vmf) * we just need to make racing faults a rare case. * * The implementation below would be much simpler if we just used a - * standard mutex or completion: but we cannot take i_mutex in fault, + * standard mutex or completion: but we cannot take i_rwsem in fault, * and bloating every shmem inode for this unlikely case would be sad. */ if (unlikely(inode->i_private)) { @@ -2482,7 +2482,7 @@ shmem_write_begin(struct file *file, struct address_space *mapping, struct shmem_inode_info *info = SHMEM_I(inode); pgoff_t index = pos >> PAGE_SHIFT; - /* i_mutex is held by caller */ + /* i_rwsem is held by caller */ if (unlikely(info->seals & (F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_FUTURE_WRITE))) { if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) @@ -2582,7 +2582,7 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to) /* * We must evaluate after, since reads (unlike writes) - * are called without i_mutex protection against truncate + * are called without i_rwsem protection against truncate */ nr = PAGE_SIZE; i_size = i_size_read(inode); @@ -2652,7 +2652,7 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence) return -ENXIO; inode_lock(inode); - /* We're holding i_mutex so we can access i_size directly */ + /* We're holding i_rwsem so we can access i_size directly */ offset = mapping_seek_hole_data(mapping, offset, inode->i_size, whence); if (offset >= 0) offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE); @@ -2681,7 +2681,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq); - /* protected by i_mutex */ + /* protected by i_rwsem */ if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) { error = -EPERM; goto out; diff --git a/mm/truncate.c b/mm/truncate.c index 234ddd879caa..0f9becee9789 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -412,7 +412,7 @@ EXPORT_SYMBOL(truncate_inode_pages_range); * @mapping: mapping to truncate * @lstart: offset from which to truncate * - * Called under (and serialised by) inode->i_mutex. + * Called under (and serialised by) inode->i_rwsem. * * Note: When this function returns, there can be a page in the process of * deletion (inside __delete_from_page_cache()) in the specified range. Thus @@ -429,7 +429,7 @@ EXPORT_SYMBOL(truncate_inode_pages); * truncate_inode_pages_final - truncate *all* pages before inode dies * @mapping: mapping to truncate * - * Called under (and serialized by) inode->i_mutex. + * Called under (and serialized by) inode->i_rwsem. * * Filesystems have to use this in the .evict_inode path to inform the * VM that this is the final truncate and the inode is going away. @@ -748,7 +748,7 @@ EXPORT_SYMBOL(truncate_pagecache); * setattr function when ATTR_SIZE is passed in. * * Must be called with a lock serializing truncates and writes (generally - * i_mutex but e.g. xfs uses a different lock) and before all filesystem + * i_rwsem but e.g. xfs uses a different lock) and before all filesystem * specific block truncation has been performed. */ void truncate_setsize(struct inode *inode, loff_t newsize) @@ -777,7 +777,7 @@ EXPORT_SYMBOL(truncate_setsize); * * The function must be called after i_size is updated so that page fault * coming after we unlock the page will already see the new i_size. - * The function must be called while we still hold i_mutex - this not only + * The function must be called while we still hold i_rwsem - this not only * makes sure i_size is stable but also that userspace cannot observe new * i_size value before we are prepared to store mmap writes at new inode size. */ From c625b4cc57d078b03fd8aa4d86c99d584a1782be Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 10 May 2021 19:13:53 +0200 Subject: [PATCH 026/177] documentation: Sync file_operations members with reality Sync listing of struct file_operations members with the real one in fs.h. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- Documentation/filesystems/locking.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index 2183fd8cc350..cdf15492c699 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -506,6 +506,7 @@ prototypes:: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); + int (*iopoll) (struct kiocb *kiocb, bool spin); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); @@ -518,12 +519,6 @@ prototypes:: int (*fsync) (struct file *, loff_t start, loff_t end, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); - ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, - loff_t *); - ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, - loff_t *); - ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, - void __user *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, @@ -536,6 +531,14 @@ prototypes:: size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *, int, loff_t, loff_t); + void (*show_fdinfo)(struct seq_file *m, struct file *f); + unsigned (*mmap_capabilities)(struct file *); + ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, + loff_t, size_t, unsigned int); + loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + loff_t len, unsigned int remap_flags); + int (*fadvise)(struct file *, loff_t, loff_t, int); locking rules: All may block. From 730633f0b7f951726e87f912a6323641f674ae34 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 28 Jan 2021 19:19:45 +0100 Subject: [PATCH 027/177] mm: Protect operations adding pages to page cache with invalidate_lock Currently, serializing operations such as page fault, read, or readahead against hole punching is rather difficult. The basic race scheme is like: fallocate(FALLOC_FL_PUNCH_HOLE) read / fault / .. truncate_inode_pages_range() Now the problem is in this way read / page fault / readahead can instantiate pages in page cache with potentially stale data (if blocks get quickly reused). Avoiding this race is not simple - page locks do not work because we want to make sure there are *no* pages in given range. inode->i_rwsem does not work because page fault happens under mmap_sem which ranks below inode->i_rwsem. Also using it for reads makes the performance for mixed read-write workloads suffer. So create a new rw_semaphore in the address_space - invalidate_lock - that protects adding of pages to page cache for page faults / reads / readahead. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- Documentation/filesystems/locking.rst | 62 ++++++++++++----- fs/inode.c | 2 + include/linux/fs.h | 33 +++++++++ mm/filemap.c | 99 ++++++++++++++++++++++----- mm/readahead.c | 2 + mm/rmap.c | 37 +++++----- mm/truncate.c | 3 +- 7 files changed, 181 insertions(+), 57 deletions(-) diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index cdf15492c699..38a3097b6f1c 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -271,19 +271,19 @@ prototypes:: locking rules: All except set_page_dirty and freepage may block -====================== ======================== ========= -ops PageLocked(page) i_rwsem -====================== ======================== ========= +====================== ======================== ========= =============== +ops PageLocked(page) i_rwsem invalidate_lock +====================== ======================== ========= =============== writepage: yes, unlocks (see below) -readpage: yes, unlocks +readpage: yes, unlocks shared writepages: set_page_dirty no -readahead: yes, unlocks -readpages: no +readahead: yes, unlocks shared +readpages: no shared write_begin: locks the page exclusive write_end: yes, unlocks exclusive bmap: -invalidatepage: yes +invalidatepage: yes exclusive releasepage: yes freepage: yes direct_IO: @@ -378,7 +378,10 @@ keep it that way and don't breed new callers. ->invalidatepage() is called when the filesystem must attempt to drop some or all of the buffers from the page when it is being truncated. It returns zero on success. If ->invalidatepage is zero, the kernel uses -block_invalidatepage() instead. +block_invalidatepage() instead. The filesystem must exclusively acquire +invalidate_lock before invalidating page cache in truncate / hole punch path +(and thus calling into ->invalidatepage) to block races between page cache +invalidation and page cache filling functions (fault, read, ...). ->releasepage() is called when the kernel is about to try to drop the buffers from the page in preparation for freeing it. It returns zero to @@ -573,6 +576,25 @@ in sys_read() and friends. the lease within the individual filesystem to record the result of the operation +->fallocate implementation must be really careful to maintain page cache +consistency when punching holes or performing other operations that invalidate +page cache contents. Usually the filesystem needs to call +truncate_inode_pages_range() to invalidate relevant range of the page cache. +However the filesystem usually also needs to update its internal (and on disk) +view of file offset -> disk block mapping. Until this update is finished, the +filesystem needs to block page faults and reads from reloading now-stale page +cache contents from the disk. Since VFS acquires mapping->invalidate_lock in +shared mode when loading pages from disk (filemap_fault(), filemap_read(), +readahead paths), the fallocate implementation must take the invalidate_lock to +prevent reloading. + +->copy_file_range and ->remap_file_range implementations need to serialize +against modifications of file data while the operation is running. For +blocking changes through write(2) and similar operations inode->i_rwsem can be +used. To block changes to file contents via a memory mapping during the +operation, the filesystem must take mapping->invalidate_lock to coordinate +with ->page_mkwrite. + dquot_operations ================ @@ -630,11 +652,11 @@ pfn_mkwrite: yes access: yes ============= ========= =========================== -->fault() is called when a previously not present pte is about -to be faulted in. The filesystem must find and return the page associated -with the passed in "pgoff" in the vm_fault structure. If it is possible that -the page may be truncated and/or invalidated, then the filesystem must lock -the page, then ensure it is not already truncated (the page lock will block +->fault() is called when a previously not present pte is about to be faulted +in. The filesystem must find and return the page associated with the passed in +"pgoff" in the vm_fault structure. If it is possible that the page may be +truncated and/or invalidated, then the filesystem must lock invalidate_lock, +then ensure the page is not already truncated (invalidate_lock will block subsequent truncate), and then return with VM_FAULT_LOCKED, and the page locked. The VM will unlock the page. @@ -647,12 +669,14 @@ page table entry. Pointer to entry associated with the page is passed in "pte" field in vm_fault structure. Pointers to entries for other offsets should be calculated relative to "pte". -->page_mkwrite() is called when a previously read-only pte is -about to become writeable. The filesystem again must ensure that there are -no truncate/invalidate races, and then return with the page locked. If -the page has been truncated, the filesystem should not look up a new page -like the ->fault() handler, but simply return with VM_FAULT_NOPAGE, which -will cause the VM to retry the fault. +->page_mkwrite() is called when a previously read-only pte is about to become +writeable. The filesystem again must ensure that there are no +truncate/invalidate races or races with operations such as ->remap_file_range +or ->copy_file_range, and then return with the page locked. Usually +mapping->invalidate_lock is suitable for proper serialization. If the page has +been truncated, the filesystem should not look up a new page like the ->fault() +handler, but simply return with VM_FAULT_NOPAGE, which will cause the VM to +retry the fault. ->pfn_mkwrite() is the same as page_mkwrite but when the pte is VM_PFNMAP or VM_MIXEDMAP with a page-less entry. Expected return is diff --git a/fs/inode.c b/fs/inode.c index c93500d84264..84c528cd1955 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -190,6 +190,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode) mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->private_data = NULL; mapping->writeback_index = 0; + __init_rwsem(&mapping->invalidate_lock, "mapping.invalidate_lock", + &sb->s_type->invalidate_lock_key); inode->i_private = NULL; inode->i_mapping = mapping; INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..90a80de37ad4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -436,6 +436,10 @@ int pagecache_write_end(struct file *, struct address_space *mapping, * struct address_space - Contents of a cacheable, mappable object. * @host: Owner, either the inode or the block_device. * @i_pages: Cached pages. + * @invalidate_lock: Guards coherency between page cache contents and + * file offset->disk block mappings in the filesystem during invalidates. + * It is also used to block modification of page cache contents through + * memory mappings. * @gfp_mask: Memory allocation flags to use for allocating pages. * @i_mmap_writable: Number of VM_SHARED mappings. * @nr_thps: Number of THPs in the pagecache (non-shmem only). @@ -453,6 +457,7 @@ int pagecache_write_end(struct file *, struct address_space *mapping, struct address_space { struct inode *host; struct xarray i_pages; + struct rw_semaphore invalidate_lock; gfp_t gfp_mask; atomic_t i_mmap_writable; #ifdef CONFIG_READ_ONLY_THP_FOR_FS @@ -814,6 +819,33 @@ static inline void inode_lock_shared_nested(struct inode *inode, unsigned subcla down_read_nested(&inode->i_rwsem, subclass); } +static inline void filemap_invalidate_lock(struct address_space *mapping) +{ + down_write(&mapping->invalidate_lock); +} + +static inline void filemap_invalidate_unlock(struct address_space *mapping) +{ + up_write(&mapping->invalidate_lock); +} + +static inline void filemap_invalidate_lock_shared(struct address_space *mapping) +{ + down_read(&mapping->invalidate_lock); +} + +static inline int filemap_invalidate_trylock_shared( + struct address_space *mapping) +{ + return down_read_trylock(&mapping->invalidate_lock); +} + +static inline void filemap_invalidate_unlock_shared( + struct address_space *mapping) +{ + up_read(&mapping->invalidate_lock); +} + void lock_two_nondirectories(struct inode *, struct inode*); void unlock_two_nondirectories(struct inode *, struct inode*); @@ -2487,6 +2519,7 @@ struct file_system_type { struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; + struct lock_class_key invalidate_lock_key; struct lock_class_key i_mutex_dir_key; }; diff --git a/mm/filemap.c b/mm/filemap.c index acf20eca2fa4..f7f9b87d2cd0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -77,7 +77,8 @@ * ->i_pages lock * * ->i_rwsem - * ->i_mmap_rwsem (truncate->unmap_mapping_range) + * ->invalidate_lock (acquired by fs in truncate path) + * ->i_mmap_rwsem (truncate->unmap_mapping_range) * * ->mmap_lock * ->i_mmap_rwsem @@ -85,7 +86,8 @@ * ->i_pages lock (arch-dependent flush_dcache_mmap_lock) * * ->mmap_lock - * ->lock_page (access_process_vm) + * ->invalidate_lock (filemap_fault) + * ->lock_page (filemap_fault, access_process_vm) * * ->i_rwsem (generic_perform_write) * ->mmap_lock (fault_in_pages_readable->do_page_fault) @@ -2368,20 +2370,30 @@ static int filemap_update_page(struct kiocb *iocb, { int error; - if (!trylock_page(page)) { - if (iocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) + if (iocb->ki_flags & IOCB_NOWAIT) { + if (!filemap_invalidate_trylock_shared(mapping)) return -EAGAIN; + } else { + filemap_invalidate_lock_shared(mapping); + } + + if (!trylock_page(page)) { + error = -EAGAIN; + if (iocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) + goto unlock_mapping; if (!(iocb->ki_flags & IOCB_WAITQ)) { + filemap_invalidate_unlock_shared(mapping); put_and_wait_on_page_locked(page, TASK_KILLABLE); return AOP_TRUNCATED_PAGE; } error = __lock_page_async(page, iocb->ki_waitq); if (error) - return error; + goto unlock_mapping; } + error = AOP_TRUNCATED_PAGE; if (!page->mapping) - goto truncated; + goto unlock; error = 0; if (filemap_range_uptodate(mapping, iocb->ki_pos, iter, page)) @@ -2392,15 +2404,13 @@ static int filemap_update_page(struct kiocb *iocb, goto unlock; error = filemap_read_page(iocb->ki_filp, mapping, page); - if (error == AOP_TRUNCATED_PAGE) - put_page(page); - return error; -truncated: - unlock_page(page); - put_page(page); - return AOP_TRUNCATED_PAGE; + goto unlock_mapping; unlock: unlock_page(page); +unlock_mapping: + filemap_invalidate_unlock_shared(mapping); + if (error == AOP_TRUNCATED_PAGE) + put_page(page); return error; } @@ -2415,6 +2425,19 @@ static int filemap_create_page(struct file *file, if (!page) return -ENOMEM; + /* + * Protect against truncate / hole punch. Grabbing invalidate_lock here + * assures we cannot instantiate and bring uptodate new pagecache pages + * after evicting page cache during truncate and before actually + * freeing blocks. Note that we could release invalidate_lock after + * inserting the page into page cache as the locked page would then be + * enough to synchronize with hole punching. But there are code paths + * such as filemap_update_page() filling in partially uptodate pages or + * ->readpages() that need to hold invalidate_lock while mapping blocks + * for IO so let's hold the lock here as well to keep locking rules + * simple. + */ + filemap_invalidate_lock_shared(mapping); error = add_to_page_cache_lru(page, mapping, index, mapping_gfp_constraint(mapping, GFP_KERNEL)); if (error == -EEXIST) @@ -2426,9 +2449,11 @@ static int filemap_create_page(struct file *file, if (error) goto error; + filemap_invalidate_unlock_shared(mapping); pagevec_add(pvec, page); return 0; error: + filemap_invalidate_unlock_shared(mapping); put_page(page); return error; } @@ -2967,6 +2992,7 @@ vm_fault_t filemap_fault(struct vm_fault *vmf) pgoff_t max_off; struct page *page; vm_fault_t ret = 0; + bool mapping_locked = false; max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); if (unlikely(offset >= max_off)) @@ -2976,25 +3002,39 @@ vm_fault_t filemap_fault(struct vm_fault *vmf) * Do we have something in the page cache already? */ page = find_get_page(mapping, offset); - if (likely(page) && !(vmf->flags & FAULT_FLAG_TRIED)) { + if (likely(page)) { /* - * We found the page, so try async readahead before - * waiting for the lock. + * We found the page, so try async readahead before waiting for + * the lock. */ - fpin = do_async_mmap_readahead(vmf, page); - } else if (!page) { + if (!(vmf->flags & FAULT_FLAG_TRIED)) + fpin = do_async_mmap_readahead(vmf, page); + if (unlikely(!PageUptodate(page))) { + filemap_invalidate_lock_shared(mapping); + mapping_locked = true; + } + } else { /* No page in the page cache at all */ count_vm_event(PGMAJFAULT); count_memcg_event_mm(vmf->vma->vm_mm, PGMAJFAULT); ret = VM_FAULT_MAJOR; fpin = do_sync_mmap_readahead(vmf); retry_find: + /* + * See comment in filemap_create_page() why we need + * invalidate_lock + */ + if (!mapping_locked) { + filemap_invalidate_lock_shared(mapping); + mapping_locked = true; + } page = pagecache_get_page(mapping, offset, FGP_CREAT|FGP_FOR_MMAP, vmf->gfp_mask); if (!page) { if (fpin) goto out_retry; + filemap_invalidate_unlock_shared(mapping); return VM_FAULT_OOM; } } @@ -3014,8 +3054,20 @@ retry_find: * We have a locked page in the page cache, now we need to check * that it's up-to-date. If not, it is going to be due to an error. */ - if (unlikely(!PageUptodate(page))) + if (unlikely(!PageUptodate(page))) { + /* + * The page was in cache and uptodate and now it is not. + * Strange but possible since we didn't hold the page lock all + * the time. Let's drop everything get the invalidate lock and + * try again. + */ + if (!mapping_locked) { + unlock_page(page); + put_page(page); + goto retry_find; + } goto page_not_uptodate; + } /* * We've made it this far and we had to drop our mmap_lock, now is the @@ -3026,6 +3078,8 @@ retry_find: unlock_page(page); goto out_retry; } + if (mapping_locked) + filemap_invalidate_unlock_shared(mapping); /* * Found the page and have a reference on it. @@ -3056,6 +3110,7 @@ page_not_uptodate: if (!error || error == AOP_TRUNCATED_PAGE) goto retry_find; + filemap_invalidate_unlock_shared(mapping); return VM_FAULT_SIGBUS; @@ -3067,6 +3122,8 @@ out_retry: */ if (page) put_page(page); + if (mapping_locked) + filemap_invalidate_unlock_shared(mapping); if (fpin) fput(fpin); return ret | VM_FAULT_RETRY; @@ -3437,6 +3494,8 @@ out: * * If the page does not get brought uptodate, return -EIO. * + * The function expects mapping->invalidate_lock to be already held. + * * Return: up to date page on success, ERR_PTR() on failure. */ struct page *read_cache_page(struct address_space *mapping, @@ -3460,6 +3519,8 @@ EXPORT_SYMBOL(read_cache_page); * * If the page does not get brought uptodate, return -EIO. * + * The function expects mapping->invalidate_lock to be already held. + * * Return: up to date page on success, ERR_PTR() on failure. */ struct page *read_cache_page_gfp(struct address_space *mapping, diff --git a/mm/readahead.c b/mm/readahead.c index d589f147f4c2..41b75d76d36e 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -192,6 +192,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl, */ unsigned int nofs = memalloc_nofs_save(); + filemap_invalidate_lock_shared(mapping); /* * Preallocate as many pages as we will need. */ @@ -236,6 +237,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl, * will then handle the error. */ read_pages(ractl, &page_pool, false); + filemap_invalidate_unlock_shared(mapping); memalloc_nofs_restore(nofs); } EXPORT_SYMBOL_GPL(page_cache_ra_unbounded); diff --git a/mm/rmap.c b/mm/rmap.c index a8b01929ab2e..86471aacc54a 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -22,24 +22,25 @@ * * inode->i_rwsem (while writing or truncating, not reading or faulting) * mm->mmap_lock - * page->flags PG_locked (lock_page) * (see hugetlbfs below) - * hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share) - * mapping->i_mmap_rwsem - * hugetlb_fault_mutex (hugetlbfs specific page fault mutex) - * anon_vma->rwsem - * mm->page_table_lock or pte_lock - * swap_lock (in swap_duplicate, swap_info_get) - * mmlist_lock (in mmput, drain_mmlist and others) - * mapping->private_lock (in __set_page_dirty_buffers) - * lock_page_memcg move_lock (in __set_page_dirty_buffers) - * i_pages lock (widely used) - * lruvec->lru_lock (in lock_page_lruvec_irq) - * inode->i_lock (in set_page_dirty's __mark_inode_dirty) - * bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) - * sb_lock (within inode_lock in fs/fs-writeback.c) - * i_pages lock (widely used, in set_page_dirty, - * in arch-dependent flush_dcache_mmap_lock, - * within bdi.wb->list_lock in __sync_single_inode) + * mapping->invalidate_lock (in filemap_fault) + * page->flags PG_locked (lock_page) * (see hugetlbfs below) + * hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share) + * mapping->i_mmap_rwsem + * hugetlb_fault_mutex (hugetlbfs specific page fault mutex) + * anon_vma->rwsem + * mm->page_table_lock or pte_lock + * swap_lock (in swap_duplicate, swap_info_get) + * mmlist_lock (in mmput, drain_mmlist and others) + * mapping->private_lock (in __set_page_dirty_buffers) + * lock_page_memcg move_lock (in __set_page_dirty_buffers) + * i_pages lock (widely used) + * lruvec->lru_lock (in lock_page_lruvec_irq) + * inode->i_lock (in set_page_dirty's __mark_inode_dirty) + * bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) + * sb_lock (within inode_lock in fs/fs-writeback.c) + * i_pages lock (widely used, in set_page_dirty, + * in arch-dependent flush_dcache_mmap_lock, + * within bdi.wb->list_lock in __sync_single_inode) * * anon_vma->rwsem,mapping->i_mmap_rwsem (memory_failure, collect_procs_anon) * ->tasklist_lock diff --git a/mm/truncate.c b/mm/truncate.c index 0f9becee9789..44ad5e515140 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -412,7 +412,8 @@ EXPORT_SYMBOL(truncate_inode_pages_range); * @mapping: mapping to truncate * @lstart: offset from which to truncate * - * Called under (and serialised by) inode->i_rwsem. + * Called under (and serialised by) inode->i_rwsem and + * mapping->invalidate_lock. * * Note: When this function returns, there can be a page in the process of * deletion (inside __delete_from_page_cache()) in the specified range. Thus From 7506ae6a7033f617ca5fea53e356fb1f7bd98010 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 24 May 2021 13:02:30 +0200 Subject: [PATCH 028/177] mm: Add functions to lock invalidate_lock for two mappings Some operations such as reflinking blocks among files will need to lock invalidate_lock for two mappings. Add helper functions to do that. Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- include/linux/fs.h | 6 ++++++ mm/filemap.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/linux/fs.h b/include/linux/fs.h index 90a80de37ad4..894ff2451793 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -849,6 +849,12 @@ static inline void filemap_invalidate_unlock_shared( void lock_two_nondirectories(struct inode *, struct inode*); void unlock_two_nondirectories(struct inode *, struct inode*); +void filemap_invalidate_lock_two(struct address_space *mapping1, + struct address_space *mapping2); +void filemap_invalidate_unlock_two(struct address_space *mapping1, + struct address_space *mapping2); + + /* * NOTE: in a 32bit arch with a preemptable kernel and * an UP compile the i_size_read/write must be atomic diff --git a/mm/filemap.c b/mm/filemap.c index f7f9b87d2cd0..0fad08331cf4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1009,6 +1009,44 @@ struct page *__page_cache_alloc(gfp_t gfp) EXPORT_SYMBOL(__page_cache_alloc); #endif +/* + * filemap_invalidate_lock_two - lock invalidate_lock for two mappings + * + * Lock exclusively invalidate_lock of any passed mapping that is not NULL. + * + * @mapping1: the first mapping to lock + * @mapping2: the second mapping to lock + */ +void filemap_invalidate_lock_two(struct address_space *mapping1, + struct address_space *mapping2) +{ + if (mapping1 > mapping2) + swap(mapping1, mapping2); + if (mapping1) + down_write(&mapping1->invalidate_lock); + if (mapping2 && mapping1 != mapping2) + down_write_nested(&mapping2->invalidate_lock, 1); +} +EXPORT_SYMBOL(filemap_invalidate_lock_two); + +/* + * filemap_invalidate_unlock_two - unlock invalidate_lock for two mappings + * + * Unlock exclusive invalidate_lock of any passed mapping that is not NULL. + * + * @mapping1: the first mapping to unlock + * @mapping2: the second mapping to unlock + */ +void filemap_invalidate_unlock_two(struct address_space *mapping1, + struct address_space *mapping2) +{ + if (mapping1) + up_write(&mapping1->invalidate_lock); + if (mapping2 && mapping1 != mapping2) + up_write(&mapping2->invalidate_lock); +} +EXPORT_SYMBOL(filemap_invalidate_unlock_two); + /* * In order to wait for pages to become available there must be * waitqueues associated with pages. By using a hash table of From d4f5258eae7b38c2a28d0a7b28a6d0a8c1f9fe8e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 4 Feb 2021 18:05:42 +0100 Subject: [PATCH 029/177] ext4: Convert to use mapping->invalidate_lock Convert ext4 to use mapping->invalidate_lock instead of its private EXT4_I(inode)->i_mmap_sem. This is mostly search-and-replace. By this conversion we fix a long standing race between hole punching and read(2) / readahead(2) paths that can lead to stale page cache contents. CC: CC: Ted Tso Acked-by: Theodore Ts'o Reviewed-by: Darrick J. Wong Signed-off-by: Jan Kara --- fs/ext4/ext4.h | 10 ---------- fs/ext4/extents.c | 25 +++++++++++++----------- fs/ext4/file.c | 13 +++++++------ fs/ext4/inode.c | 47 +++++++++++++++++----------------------------- fs/ext4/ioctl.c | 4 ++-- fs/ext4/super.c | 13 +++++-------- fs/ext4/truncate.h | 8 +++++--- 7 files changed, 50 insertions(+), 70 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3c51e243450d..7ebaf66b6e31 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1086,15 +1086,6 @@ struct ext4_inode_info { * by other means, so we have i_data_sem. */ struct rw_semaphore i_data_sem; - /* - * i_mmap_sem is for serializing page faults with truncate / punch hole - * operations. We have to make sure that new page cannot be faulted in - * a section of the inode that is being punched. We cannot easily use - * i_data_sem for this since we need protection for the whole punch - * operation and i_data_sem ranks below transaction start so we have - * to occasionally drop it. - */ - struct rw_semaphore i_mmap_sem; struct inode vfs_inode; struct jbd2_inode *jinode; @@ -2972,7 +2963,6 @@ extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode, loff_t lstart, loff_t lend); extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf); -extern vm_fault_t ext4_filemap_fault(struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern int ext4_get_projid(struct inode *inode, kprojid_t *projid); extern void ext4_da_release_space(struct inode *inode, int to_free); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 92ad64b89d9b..c33e0a2cb6c3 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4474,6 +4474,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, loff_t len, int mode) { struct inode *inode = file_inode(file); + struct address_space *mapping = file->f_mapping; handle_t *handle = NULL; unsigned int max_blocks; loff_t new_size = 0; @@ -4560,17 +4561,17 @@ static long ext4_zero_range(struct file *file, loff_t offset, * Prevent page faults from reinstantiating pages we have * released from page cache. */ - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = ext4_break_layouts(inode); if (ret) { - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); goto out_mutex; } ret = ext4_update_disksize_before_punch(inode, offset, len); if (ret) { - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); goto out_mutex; } /* Now release the pages and zero block aligned part of pages */ @@ -4579,7 +4580,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size, flags); - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); if (ret) goto out_mutex; } @@ -5221,6 +5222,7 @@ out: static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) { struct super_block *sb = inode->i_sb; + struct address_space *mapping = inode->i_mapping; ext4_lblk_t punch_start, punch_stop; handle_t *handle; unsigned int credits; @@ -5274,7 +5276,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) * Prevent page faults from reinstantiating pages we have released from * page cache. */ - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = ext4_break_layouts(inode); if (ret) @@ -5289,15 +5291,15 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) * Write tail of the last page before removed range since it will get * removed from the page cache below. */ - ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, offset); + ret = filemap_write_and_wait_range(mapping, ioffset, offset); if (ret) goto out_mmap; /* * Write data that will be shifted to preserve them when discarding * page cache below. We are also protected from pages becoming dirty - * by i_mmap_sem. + * by i_rwsem and invalidate_lock. */ - ret = filemap_write_and_wait_range(inode->i_mapping, offset + len, + ret = filemap_write_and_wait_range(mapping, offset + len, LLONG_MAX); if (ret) goto out_mmap; @@ -5350,7 +5352,7 @@ out_stop: ext4_journal_stop(handle); ext4_fc_stop_ineligible(sb); out_mmap: - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); out_mutex: inode_unlock(inode); return ret; @@ -5367,6 +5369,7 @@ out_mutex: static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) { struct super_block *sb = inode->i_sb; + struct address_space *mapping = inode->i_mapping; handle_t *handle; struct ext4_ext_path *path; struct ext4_extent *extent; @@ -5425,7 +5428,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) * Prevent page faults from reinstantiating pages we have released from * page cache. */ - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = ext4_break_layouts(inode); if (ret) @@ -5526,7 +5529,7 @@ out_stop: ext4_journal_stop(handle); ext4_fc_stop_ineligible(sb); out_mmap: - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); out_mutex: inode_unlock(inode); return ret; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 816dedcbd541..d3b4ed91aa68 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -704,22 +704,23 @@ static vm_fault_t ext4_dax_huge_fault(struct vm_fault *vmf, */ bool write = (vmf->flags & FAULT_FLAG_WRITE) && (vmf->vma->vm_flags & VM_SHARED); + struct address_space *mapping = vmf->vma->vm_file->f_mapping; pfn_t pfn; if (write) { sb_start_pagefault(sb); file_update_time(vmf->vma->vm_file); - down_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock_shared(mapping); retry: handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, EXT4_DATA_TRANS_BLOCKS(sb)); if (IS_ERR(handle)) { - up_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(mapping); sb_end_pagefault(sb); return VM_FAULT_SIGBUS; } } else { - down_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock_shared(mapping); } result = dax_iomap_fault(vmf, pe_size, &pfn, &error, &ext4_iomap_ops); if (write) { @@ -731,10 +732,10 @@ retry: /* Handling synchronous page fault? */ if (result & VM_FAULT_NEEDDSYNC) result = dax_finish_sync_fault(vmf, pe_size, pfn); - up_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(mapping); sb_end_pagefault(sb); } else { - up_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(mapping); } return result; @@ -756,7 +757,7 @@ static const struct vm_operations_struct ext4_dax_vm_ops = { #endif static const struct vm_operations_struct ext4_file_vm_ops = { - .fault = ext4_filemap_fault, + .fault = filemap_fault, .map_pages = filemap_map_pages, .page_mkwrite = ext4_page_mkwrite, }; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d8de607849df..325c038e7b23 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3950,20 +3950,19 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset, return ret; } -static void ext4_wait_dax_page(struct ext4_inode_info *ei) +static void ext4_wait_dax_page(struct inode *inode) { - up_write(&ei->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); schedule(); - down_write(&ei->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); } int ext4_break_layouts(struct inode *inode) { - struct ext4_inode_info *ei = EXT4_I(inode); struct page *page; int error; - if (WARN_ON_ONCE(!rwsem_is_locked(&ei->i_mmap_sem))) + if (WARN_ON_ONCE(!rwsem_is_locked(&inode->i_mapping->invalidate_lock))) return -EINVAL; do { @@ -3974,7 +3973,7 @@ int ext4_break_layouts(struct inode *inode) error = ___wait_var_event(&page->_refcount, atomic_read(&page->_refcount) == 1, TASK_INTERRUPTIBLE, 0, 0, - ext4_wait_dax_page(ei)); + ext4_wait_dax_page(inode)); } while (error == 0); return error; @@ -4005,9 +4004,9 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); if (ext4_has_inline_data(inode)) { - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = ext4_convert_inline_data(inode); - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); if (ret) return ret; } @@ -4058,7 +4057,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) * Prevent page faults from reinstantiating pages we have released from * page cache. */ - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = ext4_break_layouts(inode); if (ret) @@ -4131,7 +4130,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) out_stop: ext4_journal_stop(handle); out_dio: - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); out_mutex: inode_unlock(inode); return ret; @@ -5426,11 +5425,11 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, inode_dio_wait(inode); } - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); rc = ext4_break_layouts(inode); if (rc) { - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); goto err_out; } @@ -5506,7 +5505,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, error = rc; } out_mmap_sem: - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); } if (!error) { @@ -5983,10 +5982,10 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) * data (and journalled aops don't know how to handle these cases). */ if (val) { - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); err = filemap_write_and_wait(inode->i_mapping); if (err < 0) { - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); return err; } } @@ -6019,7 +6018,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) percpu_up_write(&sbi->s_writepages_rwsem); if (val) - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); /* Finally we can mark the inode as dirty. */ @@ -6063,7 +6062,7 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf) sb_start_pagefault(inode->i_sb); file_update_time(vma->vm_file); - down_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock_shared(mapping); err = ext4_convert_inline_data(inode); if (err) @@ -6176,7 +6175,7 @@ retry_alloc: out_ret: ret = block_page_mkwrite_return(err); out: - up_read(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(mapping); sb_end_pagefault(inode->i_sb); return ret; out_error: @@ -6184,15 +6183,3 @@ out_error: ext4_journal_stop(handle); goto out; } - -vm_fault_t ext4_filemap_fault(struct vm_fault *vmf) -{ - struct inode *inode = file_inode(vmf->vma->vm_file); - vm_fault_t ret; - - down_read(&EXT4_I(inode)->i_mmap_sem); - ret = filemap_fault(vmf); - up_read(&EXT4_I(inode)->i_mmap_sem); - - return ret; -} diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 6eed6170aded..4fb5fe083c2b 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -148,7 +148,7 @@ static long swap_inode_boot_loader(struct super_block *sb, goto journal_err_out; } - down_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); err = filemap_write_and_wait(inode->i_mapping); if (err) goto err_out; @@ -256,7 +256,7 @@ err_out1: ext4_double_up_write_data_sem(inode, inode_bl); err_out: - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); journal_err_out: unlock_two_nondirectories(inode, inode_bl); iput(inode_bl); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index dfa09a277b56..d6df62fc810c 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -90,12 +90,9 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb, /* * Lock ordering * - * Note the difference between i_mmap_sem (EXT4_I(inode)->i_mmap_sem) and - * i_mmap_rwsem (inode->i_mmap_rwsem)! - * * page fault path: - * mmap_lock -> sb_start_pagefault -> i_mmap_sem (r) -> transaction start -> - * page lock -> i_data_sem (rw) + * mmap_lock -> sb_start_pagefault -> invalidate_lock (r) -> transaction start + * -> page lock -> i_data_sem (rw) * * buffered write path: * sb_start_write -> i_mutex -> mmap_lock @@ -103,8 +100,9 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb, * i_data_sem (rw) * * truncate: - * sb_start_write -> i_mutex -> i_mmap_sem (w) -> i_mmap_rwsem (w) -> page lock - * sb_start_write -> i_mutex -> i_mmap_sem (w) -> transaction start -> + * sb_start_write -> i_mutex -> invalidate_lock (w) -> i_mmap_rwsem (w) -> + * page lock + * sb_start_write -> i_mutex -> invalidate_lock (w) -> transaction start -> * i_data_sem (rw) * * direct IO: @@ -1360,7 +1358,6 @@ static void init_once(void *foo) INIT_LIST_HEAD(&ei->i_orphan); init_rwsem(&ei->xattr_sem); init_rwsem(&ei->i_data_sem); - init_rwsem(&ei->i_mmap_sem); inode_init_once(&ei->vfs_inode); ext4_fc_init_inode(&ei->vfs_inode); } diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h index bcbe3668c1d4..ce84aa2786c7 100644 --- a/fs/ext4/truncate.h +++ b/fs/ext4/truncate.h @@ -11,14 +11,16 @@ */ static inline void ext4_truncate_failed_write(struct inode *inode) { + struct address_space *mapping = inode->i_mapping; + /* * We don't need to call ext4_break_layouts() because the blocks we * are truncating were never visible to userspace. */ - down_write(&EXT4_I(inode)->i_mmap_sem); - truncate_inode_pages(inode->i_mapping, inode->i_size); + filemap_invalidate_lock(mapping); + truncate_inode_pages(mapping, inode->i_size); ext4_truncate(inode); - up_write(&EXT4_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); } /* From 70f3bad8c3154ba5f241c03f9c0cd050887a119c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 12 Apr 2021 18:43:11 +0200 Subject: [PATCH 030/177] ext2: Convert to using invalidate_lock Ext2 has its private dax_sem used for synchronizing page faults and truncation. Use mapping->invalidate_lock instead as it is meant for this purpose. CC: Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ext2/ext2.h | 11 ----------- fs/ext2/file.c | 7 +++---- fs/ext2/inode.c | 12 ++++++------ fs/ext2/super.c | 3 --- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index b0a694820cb7..81907a041570 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -667,9 +667,6 @@ struct ext2_inode_info { struct rw_semaphore xattr_sem; #endif rwlock_t i_meta_lock; -#ifdef CONFIG_FS_DAX - struct rw_semaphore dax_sem; -#endif /* * truncate_mutex is for serialising ext2_truncate() against @@ -685,14 +682,6 @@ struct ext2_inode_info { #endif }; -#ifdef CONFIG_FS_DAX -#define dax_sem_down_write(ext2_inode) down_write(&(ext2_inode)->dax_sem) -#define dax_sem_up_write(ext2_inode) up_write(&(ext2_inode)->dax_sem) -#else -#define dax_sem_down_write(ext2_inode) -#define dax_sem_up_write(ext2_inode) -#endif - /* * Inode dynamic state flags */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index f98466acc672..eb97aa3d700e 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -81,7 +81,7 @@ out_unlock: * * mmap_lock (MM) * sb_start_pagefault (vfs, freeze) - * ext2_inode_info->dax_sem + * address_space->invalidate_lock * address_space->i_mmap_rwsem or page_lock (mutually exclusive in DAX) * ext2_inode_info->truncate_mutex * @@ -91,7 +91,6 @@ out_unlock: static vm_fault_t ext2_dax_fault(struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); - struct ext2_inode_info *ei = EXT2_I(inode); vm_fault_t ret; bool write = (vmf->flags & FAULT_FLAG_WRITE) && (vmf->vma->vm_flags & VM_SHARED); @@ -100,11 +99,11 @@ static vm_fault_t ext2_dax_fault(struct vm_fault *vmf) sb_start_pagefault(inode->i_sb); file_update_time(vmf->vma->vm_file); } - down_read(&ei->dax_sem); + filemap_invalidate_lock_shared(inode->i_mapping); ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, NULL, &ext2_iomap_ops); - up_read(&ei->dax_sem); + filemap_invalidate_unlock_shared(inode->i_mapping); if (write) sb_end_pagefault(inode->i_sb); return ret; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index dadb121beb22..33f874f0fc4a 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1177,7 +1177,7 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de ext2_free_data(inode, p, q); } -/* dax_sem must be held when calling this function */ +/* mapping->invalidate_lock must be held when calling this function */ static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) { __le32 *i_data = EXT2_I(inode)->i_data; @@ -1194,7 +1194,7 @@ static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); #ifdef CONFIG_FS_DAX - WARN_ON(!rwsem_is_locked(&ei->dax_sem)); + WARN_ON(!rwsem_is_locked(&inode->i_mapping->invalidate_lock)); #endif n = ext2_block_to_path(inode, iblock, offsets, NULL); @@ -1276,9 +1276,9 @@ static void ext2_truncate_blocks(struct inode *inode, loff_t offset) if (ext2_inode_is_fast_symlink(inode)) return; - dax_sem_down_write(EXT2_I(inode)); + filemap_invalidate_lock(inode->i_mapping); __ext2_truncate_blocks(inode, offset); - dax_sem_up_write(EXT2_I(inode)); + filemap_invalidate_unlock(inode->i_mapping); } static int ext2_setsize(struct inode *inode, loff_t newsize) @@ -1308,10 +1308,10 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) if (error) return error; - dax_sem_down_write(EXT2_I(inode)); + filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, newsize); __ext2_truncate_blocks(inode, newsize); - dax_sem_up_write(EXT2_I(inode)); + filemap_invalidate_unlock(inode->i_mapping); inode->i_mtime = inode->i_ctime = current_time(inode); if (inode_needs_sync(inode)) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 21e09fbaa46f..987bcf32ed46 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -206,9 +206,6 @@ static void init_once(void *foo) init_rwsem(&ei->xattr_sem); #endif mutex_init(&ei->truncate_mutex); -#ifdef CONFIG_FS_DAX - init_rwsem(&ei->dax_sem); -#endif inode_init_once(&ei->vfs_inode); } From e31cbde7ecdcfdf22eac6fd37e63548adacc4ede Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Fri, 16 Oct 2020 04:10:02 +0200 Subject: [PATCH 031/177] xfs: Refactor xfs_isilocked() Introduce a new __xfs_rwsem_islocked predicate to encapsulate checking the state of a rw_semaphore, then refactor xfs_isilocked to use it. Signed-off-by: Pavel Reichl Suggested-by: Dave Chinner Suggested-by: Eric Sandeen Suggested-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Jan Kara --- fs/xfs/xfs_inode.c | 34 ++++++++++++++++++++++++++-------- fs/xfs/xfs_inode.h | 2 +- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index a835ceb79ba5..359e2cd44ad7 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -343,9 +343,29 @@ xfs_ilock_demote( } #if defined(DEBUG) || defined(XFS_WARN) -int +static inline bool +__xfs_rwsem_islocked( + struct rw_semaphore *rwsem, + bool shared) +{ + if (!debug_locks) + return rwsem_is_locked(rwsem); + + if (!shared) + return lockdep_is_held_type(rwsem, 0); + + /* + * We are checking that the lock is held at least in shared + * mode but don't care that it might be held exclusively + * (i.e. shared | excl). Hence we check if the lock is held + * in any mode rather than an explicit shared mode. + */ + return lockdep_is_held_type(rwsem, -1); +} + +bool xfs_isilocked( - xfs_inode_t *ip, + struct xfs_inode *ip, uint lock_flags) { if (lock_flags & (XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)) { @@ -360,15 +380,13 @@ xfs_isilocked( return rwsem_is_locked(&ip->i_mmaplock.mr_lock); } - if (lock_flags & (XFS_IOLOCK_EXCL|XFS_IOLOCK_SHARED)) { - if (!(lock_flags & XFS_IOLOCK_SHARED)) - return !debug_locks || - lockdep_is_held_type(&VFS_I(ip)->i_rwsem, 0); - return rwsem_is_locked(&VFS_I(ip)->i_rwsem); + if (lock_flags & (XFS_IOLOCK_EXCL | XFS_IOLOCK_SHARED)) { + return __xfs_rwsem_islocked(&VFS_I(ip)->i_rwsem, + (lock_flags & XFS_IOLOCK_SHARED)); } ASSERT(0); - return 0; + return false; } #endif diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 4b6703dbffb8..4b5202ae8ebb 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -410,7 +410,7 @@ void xfs_ilock(xfs_inode_t *, uint); int xfs_ilock_nowait(xfs_inode_t *, uint); void xfs_iunlock(xfs_inode_t *, uint); void xfs_ilock_demote(xfs_inode_t *, uint); -int xfs_isilocked(xfs_inode_t *, uint); +bool xfs_isilocked(struct xfs_inode *, uint); uint xfs_ilock_data_map_shared(struct xfs_inode *); uint xfs_ilock_attr_map_shared(struct xfs_inode *); From 2433480a7e1d0c057442b284c336cfaa61523117 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 12 Apr 2021 18:56:24 +0200 Subject: [PATCH 032/177] xfs: Convert to use invalidate_lock Use invalidate_lock instead of XFS internal i_mmap_lock. The intended purpose of invalidate_lock is exactly the same. Note that the locking in __xfs_filemap_fault() slightly changes as filemap_fault() already takes invalidate_lock. Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong CC: CC: "Darrick J. Wong" Signed-off-by: Jan Kara --- fs/xfs/xfs_file.c | 13 +++++++----- fs/xfs/xfs_inode.c | 50 ++++++++++++++++++++++++---------------------- fs/xfs/xfs_inode.h | 1 - fs/xfs/xfs_super.c | 2 -- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index cc3cfb12df53..3dfbdcdb0d1c 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1302,7 +1302,7 @@ xfs_file_llseek( * * mmap_lock (MM) * sb_start_pagefault(vfs, freeze) - * i_mmaplock (XFS - truncate serialisation) + * invalidate_lock (vfs/XFS_MMAPLOCK - truncate serialisation) * page_lock (MM) * i_lock (XFS - extent map serialisation) */ @@ -1323,24 +1323,27 @@ __xfs_filemap_fault( file_update_time(vmf->vma->vm_file); } - xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (IS_DAX(inode)) { pfn_t pfn; + xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); ret = dax_iomap_fault(vmf, pe_size, &pfn, NULL, (write_fault && !vmf->cow_page) ? &xfs_direct_write_iomap_ops : &xfs_read_iomap_ops); if (ret & VM_FAULT_NEEDDSYNC) ret = dax_finish_sync_fault(vmf, pe_size, pfn); + xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); } else { - if (write_fault) + if (write_fault) { + xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); ret = iomap_page_mkwrite(vmf, &xfs_buffered_write_iomap_ops); - else + xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); + } else { ret = filemap_fault(vmf); + } } - xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (write_fault) sb_end_pagefault(inode->i_sb); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 359e2cd44ad7..d6a8ac76b45d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -132,7 +132,7 @@ xfs_ilock_attr_map_shared( /* * In addition to i_rwsem in the VFS inode, the xfs inode contains 2 - * multi-reader locks: i_mmap_lock and the i_lock. This routine allows + * multi-reader locks: invalidate_lock and the i_lock. This routine allows * various combinations of the locks to be obtained. * * The 3 locks should always be ordered so that the IO lock is obtained first, @@ -140,23 +140,23 @@ xfs_ilock_attr_map_shared( * * Basic locking order: * - * i_rwsem -> i_mmap_lock -> page_lock -> i_ilock + * i_rwsem -> invalidate_lock -> page_lock -> i_ilock * * mmap_lock locking order: * * i_rwsem -> page lock -> mmap_lock - * mmap_lock -> i_mmap_lock -> page_lock + * mmap_lock -> invalidate_lock -> page_lock * * The difference in mmap_lock locking order mean that we cannot hold the - * i_mmap_lock over syscall based read(2)/write(2) based IO. These IO paths can - * fault in pages during copy in/out (for buffered IO) or require the mmap_lock - * in get_user_pages() to map the user pages into the kernel address space for - * direct IO. Similarly the i_rwsem cannot be taken inside a page fault because - * page faults already hold the mmap_lock. + * invalidate_lock over syscall based read(2)/write(2) based IO. These IO paths + * can fault in pages during copy in/out (for buffered IO) or require the + * mmap_lock in get_user_pages() to map the user pages into the kernel address + * space for direct IO. Similarly the i_rwsem cannot be taken inside a page + * fault because page faults already hold the mmap_lock. * * Hence to serialise fully against both syscall and mmap based IO, we need to - * take both the i_rwsem and the i_mmap_lock. These locks should *only* be both - * taken in places where we need to invalidate the page cache in a race + * take both the i_rwsem and the invalidate_lock. These locks should *only* be + * both taken in places where we need to invalidate the page cache in a race * free manner (e.g. truncate, hole punch and other extent manipulation * functions). */ @@ -188,10 +188,13 @@ xfs_ilock( XFS_IOLOCK_DEP(lock_flags)); } - if (lock_flags & XFS_MMAPLOCK_EXCL) - mrupdate_nested(&ip->i_mmaplock, XFS_MMAPLOCK_DEP(lock_flags)); - else if (lock_flags & XFS_MMAPLOCK_SHARED) - mraccess_nested(&ip->i_mmaplock, XFS_MMAPLOCK_DEP(lock_flags)); + if (lock_flags & XFS_MMAPLOCK_EXCL) { + down_write_nested(&VFS_I(ip)->i_mapping->invalidate_lock, + XFS_MMAPLOCK_DEP(lock_flags)); + } else if (lock_flags & XFS_MMAPLOCK_SHARED) { + down_read_nested(&VFS_I(ip)->i_mapping->invalidate_lock, + XFS_MMAPLOCK_DEP(lock_flags)); + } if (lock_flags & XFS_ILOCK_EXCL) mrupdate_nested(&ip->i_lock, XFS_ILOCK_DEP(lock_flags)); @@ -240,10 +243,10 @@ xfs_ilock_nowait( } if (lock_flags & XFS_MMAPLOCK_EXCL) { - if (!mrtryupdate(&ip->i_mmaplock)) + if (!down_write_trylock(&VFS_I(ip)->i_mapping->invalidate_lock)) goto out_undo_iolock; } else if (lock_flags & XFS_MMAPLOCK_SHARED) { - if (!mrtryaccess(&ip->i_mmaplock)) + if (!down_read_trylock(&VFS_I(ip)->i_mapping->invalidate_lock)) goto out_undo_iolock; } @@ -258,9 +261,9 @@ xfs_ilock_nowait( out_undo_mmaplock: if (lock_flags & XFS_MMAPLOCK_EXCL) - mrunlock_excl(&ip->i_mmaplock); + up_write(&VFS_I(ip)->i_mapping->invalidate_lock); else if (lock_flags & XFS_MMAPLOCK_SHARED) - mrunlock_shared(&ip->i_mmaplock); + up_read(&VFS_I(ip)->i_mapping->invalidate_lock); out_undo_iolock: if (lock_flags & XFS_IOLOCK_EXCL) up_write(&VFS_I(ip)->i_rwsem); @@ -307,9 +310,9 @@ xfs_iunlock( up_read(&VFS_I(ip)->i_rwsem); if (lock_flags & XFS_MMAPLOCK_EXCL) - mrunlock_excl(&ip->i_mmaplock); + up_write(&VFS_I(ip)->i_mapping->invalidate_lock); else if (lock_flags & XFS_MMAPLOCK_SHARED) - mrunlock_shared(&ip->i_mmaplock); + up_read(&VFS_I(ip)->i_mapping->invalidate_lock); if (lock_flags & XFS_ILOCK_EXCL) mrunlock_excl(&ip->i_lock); @@ -335,7 +338,7 @@ xfs_ilock_demote( if (lock_flags & XFS_ILOCK_EXCL) mrdemote(&ip->i_lock); if (lock_flags & XFS_MMAPLOCK_EXCL) - mrdemote(&ip->i_mmaplock); + downgrade_write(&VFS_I(ip)->i_mapping->invalidate_lock); if (lock_flags & XFS_IOLOCK_EXCL) downgrade_write(&VFS_I(ip)->i_rwsem); @@ -375,9 +378,8 @@ xfs_isilocked( } if (lock_flags & (XFS_MMAPLOCK_EXCL|XFS_MMAPLOCK_SHARED)) { - if (!(lock_flags & XFS_MMAPLOCK_SHARED)) - return !!ip->i_mmaplock.mr_writer; - return rwsem_is_locked(&ip->i_mmaplock.mr_lock); + return __xfs_rwsem_islocked(&VFS_I(ip)->i_rwsem, + (lock_flags & XFS_IOLOCK_SHARED)); } if (lock_flags & (XFS_IOLOCK_EXCL | XFS_IOLOCK_SHARED)) { diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 4b5202ae8ebb..e0ae905554e2 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -40,7 +40,6 @@ typedef struct xfs_inode { /* Transaction and locking information. */ struct xfs_inode_log_item *i_itemp; /* logging information */ mrlock_t i_lock; /* inode lock */ - mrlock_t i_mmaplock; /* inode mmap IO lock */ atomic_t i_pincount; /* inode pin count */ /* diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 2c9e26a44546..102cbd606633 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -709,8 +709,6 @@ xfs_fs_inode_init_once( atomic_set(&ip->i_pincount, 0); spin_lock_init(&ip->i_flags_lock); - mrlock_init(&ip->i_mmaplock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER, - "xfsino", ip->i_ino); mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER, "xfsino", ip->i_ino); } From d2c292d84c4983424938f32c9c247f6ab8719769 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 24 May 2021 13:17:49 +0200 Subject: [PATCH 033/177] xfs: Convert double locking of MMAPLOCK to use VFS helpers Convert places in XFS that take MMAPLOCK for two inodes to use helper VFS provides for it (filemap_invalidate_down_write_two()). Note that this changes lock ordering for MMAPLOCK from inode number based ordering to pointer based ordering VFS generally uses. CC: "Darrick J. Wong" Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/xfs/xfs_bmap_util.c | 15 ++++++++------- fs/xfs/xfs_inode.c | 37 +++++++++++-------------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 213a97a921bb..1cd3f940fa6a 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1626,7 +1626,6 @@ xfs_swap_extents( struct xfs_bstat *sbp = &sxp->sx_stat; int src_log_flags, target_log_flags; int error = 0; - int lock_flags; uint64_t f; int resblks = 0; unsigned int flags = 0; @@ -1638,8 +1637,8 @@ xfs_swap_extents( * do the rest of the checks. */ lock_two_nondirectories(VFS_I(ip), VFS_I(tip)); - lock_flags = XFS_MMAPLOCK_EXCL; - xfs_lock_two_inodes(ip, XFS_MMAPLOCK_EXCL, tip, XFS_MMAPLOCK_EXCL); + filemap_invalidate_lock_two(VFS_I(ip)->i_mapping, + VFS_I(tip)->i_mapping); /* Verify that both files have the same format */ if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) { @@ -1711,7 +1710,6 @@ xfs_swap_extents( * or cancel will unlock the inodes from this point onwards. */ xfs_lock_two_inodes(ip, XFS_ILOCK_EXCL, tip, XFS_ILOCK_EXCL); - lock_flags |= XFS_ILOCK_EXCL; xfs_trans_ijoin(tp, ip, 0); xfs_trans_ijoin(tp, tip, 0); @@ -1830,13 +1828,16 @@ xfs_swap_extents( trace_xfs_swap_extent_after(ip, 0); trace_xfs_swap_extent_after(tip, 1); +out_unlock_ilock: + xfs_iunlock(ip, XFS_ILOCK_EXCL); + xfs_iunlock(tip, XFS_ILOCK_EXCL); out_unlock: - xfs_iunlock(ip, lock_flags); - xfs_iunlock(tip, lock_flags); + filemap_invalidate_unlock_two(VFS_I(ip)->i_mapping, + VFS_I(tip)->i_mapping); unlock_two_nondirectories(VFS_I(ip), VFS_I(tip)); return error; out_trans_cancel: xfs_trans_cancel(tp); - goto out_unlock; + goto out_unlock_ilock; } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d6a8ac76b45d..3c0bb21453dc 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -552,12 +552,10 @@ again: } /* - * xfs_lock_two_inodes() can only be used to lock one type of lock at a time - - * the mmaplock or the ilock, but not more than one type at a time. If we lock - * more than one at a time, lockdep will report false positives saying we have - * violated locking orders. The iolock must be double-locked separately since - * we use i_rwsem for that. We now support taking one lock EXCL and the other - * SHARED. + * xfs_lock_two_inodes() can only be used to lock ilock. The iolock and + * mmaplock must be double-locked separately since we use i_rwsem and + * invalidate_lock for that. We now support taking one lock EXCL and the + * other SHARED. */ void xfs_lock_two_inodes( @@ -575,15 +573,8 @@ xfs_lock_two_inodes( ASSERT(hweight32(ip1_mode) == 1); ASSERT(!(ip0_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL))); ASSERT(!(ip1_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL))); - ASSERT(!(ip0_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) || - !(ip0_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))); - ASSERT(!(ip1_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) || - !(ip1_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))); - ASSERT(!(ip1_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) || - !(ip0_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))); - ASSERT(!(ip0_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) || - !(ip1_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))); - + ASSERT(!(ip0_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL))); + ASSERT(!(ip1_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL))); ASSERT(ip0->i_ino != ip1->i_ino); if (ip0->i_ino > ip1->i_ino) { @@ -3748,11 +3739,8 @@ xfs_ilock2_io_mmap( ret = xfs_iolock_two_inodes_and_break_layout(VFS_I(ip1), VFS_I(ip2)); if (ret) return ret; - if (ip1 == ip2) - xfs_ilock(ip1, XFS_MMAPLOCK_EXCL); - else - xfs_lock_two_inodes(ip1, XFS_MMAPLOCK_EXCL, - ip2, XFS_MMAPLOCK_EXCL); + filemap_invalidate_lock_two(VFS_I(ip1)->i_mapping, + VFS_I(ip2)->i_mapping); return 0; } @@ -3762,12 +3750,9 @@ xfs_iunlock2_io_mmap( struct xfs_inode *ip1, struct xfs_inode *ip2) { - bool same_inode = (ip1 == ip2); - - xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL); - if (!same_inode) - xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL); + filemap_invalidate_unlock_two(VFS_I(ip1)->i_mapping, + VFS_I(ip2)->i_mapping); inode_unlock(VFS_I(ip2)); - if (!same_inode) + if (ip1 != ip2) inode_unlock(VFS_I(ip1)); } From 448f94909eb7056e53c882b82514ea4f3adcf544 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Apr 2021 09:38:27 +0200 Subject: [PATCH 034/177] zonefs: Convert to using invalidate_lock Use invalidate_lock instead of zonefs' private i_mmap_sem. The intended purpose is exactly the same. CC: Damien Le Moal CC: Johannes Thumshirn CC: Acked-by: Damien Le Moal Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/zonefs/super.c | 23 +++++------------------ fs/zonefs/zonefs.h | 7 +++---- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index dbf03635869c..f323bf3b0e82 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -462,7 +462,7 @@ static int zonefs_file_truncate(struct inode *inode, loff_t isize) inode_dio_wait(inode); /* Serialize against page faults */ - down_write(&zi->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); /* Serialize against zonefs_iomap_begin() */ mutex_lock(&zi->i_truncate_mutex); @@ -500,7 +500,7 @@ static int zonefs_file_truncate(struct inode *inode, loff_t isize) unlock: mutex_unlock(&zi->i_truncate_mutex); - up_write(&zi->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); return ret; } @@ -575,18 +575,6 @@ static int zonefs_file_fsync(struct file *file, loff_t start, loff_t end, return ret; } -static vm_fault_t zonefs_filemap_fault(struct vm_fault *vmf) -{ - struct zonefs_inode_info *zi = ZONEFS_I(file_inode(vmf->vma->vm_file)); - vm_fault_t ret; - - down_read(&zi->i_mmap_sem); - ret = filemap_fault(vmf); - up_read(&zi->i_mmap_sem); - - return ret; -} - static vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); @@ -607,16 +595,16 @@ static vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) file_update_time(vmf->vma->vm_file); /* Serialize against truncates */ - down_read(&zi->i_mmap_sem); + filemap_invalidate_lock_shared(inode->i_mapping); ret = iomap_page_mkwrite(vmf, &zonefs_iomap_ops); - up_read(&zi->i_mmap_sem); + filemap_invalidate_unlock_shared(inode->i_mapping); sb_end_pagefault(inode->i_sb); return ret; } static const struct vm_operations_struct zonefs_file_vm_ops = { - .fault = zonefs_filemap_fault, + .fault = filemap_fault, .map_pages = filemap_map_pages, .page_mkwrite = zonefs_filemap_page_mkwrite, }; @@ -1158,7 +1146,6 @@ static struct inode *zonefs_alloc_inode(struct super_block *sb) inode_init_once(&zi->i_vnode); mutex_init(&zi->i_truncate_mutex); - init_rwsem(&zi->i_mmap_sem); zi->i_wr_refcnt = 0; return &zi->i_vnode; diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h index 51141907097c..7b147907c328 100644 --- a/fs/zonefs/zonefs.h +++ b/fs/zonefs/zonefs.h @@ -70,12 +70,11 @@ struct zonefs_inode_info { * and changes to the inode private data, and in particular changes to * a sequential file size on completion of direct IO writes. * Serialization of mmap read IOs with truncate and syscall IO - * operations is done with i_mmap_sem in addition to i_truncate_mutex. - * Only zonefs_seq_file_truncate() takes both lock (i_mmap_sem first, - * i_truncate_mutex second). + * operations is done with invalidate_lock in addition to + * i_truncate_mutex. Only zonefs_seq_file_truncate() takes both lock + * (invalidate_lock first, i_truncate_mutex second). */ struct mutex i_truncate_mutex; - struct rw_semaphore i_mmap_sem; /* guarded by i_truncate_mutex */ unsigned int i_wr_refcnt; From edc6d01bad7331b376a1a8f5c6d8e9221e9f9f37 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Apr 2021 18:10:37 +0200 Subject: [PATCH 035/177] f2fs: Convert to using invalidate_lock Use invalidate_lock instead of f2fs' private i_mmap_sem. The intended purpose is exactly the same. By this conversion we fix a long standing race between hole punching and read(2) / readahead(2) paths that can lead to stale page cache contents. CC: Jaegeuk Kim CC: Chao Yu CC: linux-f2fs-devel@lists.sourceforge.net Acked-by: Chao Yu Signed-off-by: Jan Kara --- fs/f2fs/data.c | 8 +++---- fs/f2fs/f2fs.h | 1 - fs/f2fs/file.c | 62 ++++++++++++++++++++++++------------------------- fs/f2fs/super.c | 1 - 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d2cf48c5a2e4..eb222b35edef 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3187,12 +3187,12 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to) /* In the fs-verity case, f2fs_end_enable_verity() does the truncate */ if (to > i_size && !f2fs_verity_in_progress(inode)) { down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); truncate_pagecache(inode, i_size); f2fs_truncate_blocks(inode, i_size, true); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); } } @@ -3852,7 +3852,7 @@ static int f2fs_migrate_blocks(struct inode *inode, block_t start_blk, int ret = 0; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); set_inode_flag(inode, FI_ALIGNED_WRITE); @@ -3894,7 +3894,7 @@ done: clear_inode_flag(inode, FI_DO_DEFRAG); clear_inode_flag(inode, FI_ALIGNED_WRITE); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); return ret; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ee8eb33e2c25..906b2c4b50e7 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -754,7 +754,6 @@ struct f2fs_inode_info { /* avoid racing between foreground op and gc */ struct rw_semaphore i_gc_rwsem[2]; - struct rw_semaphore i_mmap_sem; struct rw_semaphore i_xattr_sem; /* avoid racing between reading and changing EAs */ int i_extra_isize; /* size of extra space located in i_addr */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6afd4562335f..1ff333755721 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -38,10 +38,7 @@ static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf) struct inode *inode = file_inode(vmf->vma->vm_file); vm_fault_t ret; - down_read(&F2FS_I(inode)->i_mmap_sem); ret = filemap_fault(vmf); - up_read(&F2FS_I(inode)->i_mmap_sem); - if (!ret) f2fs_update_iostat(F2FS_I_SB(inode), APP_MAPPED_READ_IO, F2FS_BLKSIZE); @@ -101,7 +98,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); file_update_time(vmf->vma->vm_file); - down_read(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock_shared(inode->i_mapping); lock_page(page); if (unlikely(page->mapping != inode->i_mapping || page_offset(page) > i_size_read(inode) || @@ -159,7 +156,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) trace_f2fs_vm_page_mkwrite(page, DATA); out_sem: - up_read(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(inode->i_mapping); sb_end_pagefault(inode->i_sb); err: @@ -940,7 +937,7 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, } down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, attr->ia_size); @@ -950,7 +947,7 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, * do not trim all blocks after i_size if target size is * larger than i_size. */ - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); if (err) return err; @@ -1095,7 +1092,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) blk_end = (loff_t)pg_end << PAGE_SHIFT; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); truncate_inode_pages_range(mapping, blk_start, blk_end - 1); @@ -1104,7 +1101,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) ret = f2fs_truncate_hole(inode, pg_start, pg_end); f2fs_unlock_op(sbi); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); } } @@ -1339,7 +1336,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) /* avoid gc operation during block exchange */ down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); f2fs_lock_op(sbi); f2fs_drop_extent_tree(inode); @@ -1347,7 +1344,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true); f2fs_unlock_op(sbi); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); return ret; } @@ -1378,13 +1375,13 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) return ret; /* write out all moved pages, if possible */ - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); truncate_pagecache(inode, offset); new_size = i_size_read(inode) - len; ret = f2fs_truncate_blocks(inode, new_size, true); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); if (!ret) f2fs_i_size_write(inode, new_size); return ret; @@ -1484,7 +1481,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, pgoff_t end; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); truncate_pagecache_range(inode, (loff_t)index << PAGE_SHIFT, @@ -1496,7 +1493,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); if (ret) { f2fs_unlock_op(sbi); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); goto out; } @@ -1508,7 +1505,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); f2fs_balance_fs(sbi, dn.node_changed); @@ -1543,6 +1540,7 @@ out: static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct address_space *mapping = inode->i_mapping; pgoff_t nr, pg_start, pg_end, delta, idx; loff_t new_size; int ret = 0; @@ -1565,14 +1563,14 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi, true); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = f2fs_truncate_blocks(inode, i_size_read(inode), true); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); if (ret) return ret; /* write out all dirty pages from offset */ - ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + ret = filemap_write_and_wait_range(mapping, offset, LLONG_MAX); if (ret) return ret; @@ -1583,7 +1581,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) /* avoid gc operation during block exchange */ down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); truncate_pagecache(inode, offset); while (!ret && idx > pg_start) { @@ -1599,14 +1597,14 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) idx + delta, nr, false); f2fs_unlock_op(sbi); } - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); /* write out all moved pages, if possible */ - down_write(&F2FS_I(inode)->i_mmap_sem); - filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + filemap_invalidate_lock(mapping); + filemap_write_and_wait_range(mapping, offset, LLONG_MAX); truncate_pagecache(inode, offset); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); if (!ret) f2fs_i_size_write(inode, new_size); @@ -3440,7 +3438,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) goto out; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3476,7 +3474,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) } up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); out: inode_unlock(inode); @@ -3593,7 +3591,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) } down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3629,7 +3627,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) } up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); if (ret >= 0) { clear_inode_flag(inode, FI_COMPRESS_RELEASED); @@ -3748,7 +3746,7 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg) goto err; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(mapping); ret = filemap_write_and_wait_range(mapping, range.start, to_end ? LLONG_MAX : end_addr - 1); @@ -3835,7 +3833,7 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg) ret = f2fs_secure_erase(prev_bdev, inode, prev_index, prev_block, len, range.flags); out: - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); err: inode_unlock(inode); @@ -4313,9 +4311,9 @@ write: /* if we couldn't write data, we should deallocate blocks. */ if (preallocated && i_size_read(inode) < target_size) { down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - down_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); f2fs_truncate(inode); - up_write(&F2FS_I(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8fecd3050ccd..ce2ab1b85c11 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1289,7 +1289,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) mutex_init(&fi->inmem_lock); init_rwsem(&fi->i_gc_rwsem[READ]); init_rwsem(&fi->i_gc_rwsem[WRITE]); - init_rwsem(&fi->i_mmap_sem); init_rwsem(&fi->i_xattr_sem); /* Will be used by directory only */ From 8bcbbe9c7c8e49281fc2e0a6c5455b87c85a9c2a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 21 Apr 2021 17:18:39 +0200 Subject: [PATCH 036/177] fuse: Convert to using invalidate_lock Use invalidate_lock instead of fuse's private i_mmap_sem. The intended purpose is exactly the same. By this conversion we fix a long standing race between hole punching and read(2) / readahead(2) paths that can lead to stale page cache contents. CC: Miklos Szeredi Reviewed-by: Miklos Szeredi Signed-off-by: Jan Kara --- fs/fuse/dax.c | 50 +++++++++++++++++++++++------------------------- fs/fuse/dir.c | 11 ++++++----- fs/fuse/file.c | 10 +++++----- fs/fuse/fuse_i.h | 7 ------- fs/fuse/inode.c | 1 - 5 files changed, 35 insertions(+), 44 deletions(-) diff --git a/fs/fuse/dax.c b/fs/fuse/dax.c index e55723744f58..fc05ce59579d 100644 --- a/fs/fuse/dax.c +++ b/fs/fuse/dax.c @@ -444,12 +444,12 @@ static int fuse_setup_new_dax_mapping(struct inode *inode, loff_t pos, /* * Can't do inline reclaim in fault path. We call * dax_layout_busy_page() before we free a range. And - * fuse_wait_dax_page() drops fi->i_mmap_sem lock and requires it. - * In fault path we enter with fi->i_mmap_sem held and can't drop - * it. Also in fault path we hold fi->i_mmap_sem shared and not - * exclusive, so that creates further issues with fuse_wait_dax_page(). - * Hence return -EAGAIN and fuse_dax_fault() will wait for a memory - * range to become free and retry. + * fuse_wait_dax_page() drops mapping->invalidate_lock and requires it. + * In fault path we enter with mapping->invalidate_lock held and can't + * drop it. Also in fault path we hold mapping->invalidate_lock shared + * and not exclusive, so that creates further issues with + * fuse_wait_dax_page(). Hence return -EAGAIN and fuse_dax_fault() + * will wait for a memory range to become free and retry. */ if (flags & IOMAP_FAULT) { alloc_dmap = alloc_dax_mapping(fcd); @@ -513,7 +513,7 @@ static int fuse_upgrade_dax_mapping(struct inode *inode, loff_t pos, down_write(&fi->dax->sem); node = interval_tree_iter_first(&fi->dax->tree, idx, idx); - /* We are holding either inode lock or i_mmap_sem, and that should + /* We are holding either inode lock or invalidate_lock, and that should * ensure that dmap can't be truncated. We are holding a reference * on dmap and that should make sure it can't be reclaimed. So dmap * should still be there in tree despite the fact we dropped and @@ -660,14 +660,12 @@ static const struct iomap_ops fuse_iomap_ops = { static void fuse_wait_dax_page(struct inode *inode) { - struct fuse_inode *fi = get_fuse_inode(inode); - - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); schedule(); - down_write(&fi->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); } -/* Should be called with fi->i_mmap_sem lock held exclusively */ +/* Should be called with mapping->invalidate_lock held exclusively */ static int __fuse_dax_break_layouts(struct inode *inode, bool *retry, loff_t start, loff_t end) { @@ -813,18 +811,18 @@ retry: * we do not want any read/write/mmap to make progress and try * to populate page cache or access memory we are trying to free. */ - down_read(&get_fuse_inode(inode)->i_mmap_sem); + filemap_invalidate_lock_shared(inode->i_mapping); ret = dax_iomap_fault(vmf, pe_size, &pfn, &error, &fuse_iomap_ops); if ((ret & VM_FAULT_ERROR) && error == -EAGAIN) { error = 0; retry = true; - up_read(&get_fuse_inode(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(inode->i_mapping); goto retry; } if (ret & VM_FAULT_NEEDDSYNC) ret = dax_finish_sync_fault(vmf, pe_size, pfn); - up_read(&get_fuse_inode(inode)->i_mmap_sem); + filemap_invalidate_unlock_shared(inode->i_mapping); if (write) sb_end_pagefault(sb); @@ -960,7 +958,7 @@ inode_inline_reclaim_one_dmap(struct fuse_conn_dax *fcd, struct inode *inode, int ret; struct interval_tree_node *node; - down_write(&fi->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); /* Lookup a dmap and corresponding file offset to reclaim. */ down_read(&fi->dax->sem); @@ -1021,7 +1019,7 @@ inode_inline_reclaim_one_dmap(struct fuse_conn_dax *fcd, struct inode *inode, out_write_dmap_sem: up_write(&fi->dax->sem); out_mmap_sem: - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); return dmap; } @@ -1050,10 +1048,10 @@ alloc_dax_mapping_reclaim(struct fuse_conn_dax *fcd, struct inode *inode) * had a reference or some other temporary failure, * Try again. We want to give up inline reclaim only * if there is no range assigned to this node. Otherwise - * if a deadlock is possible if we sleep with fi->i_mmap_sem - * held and worker to free memory can't make progress due - * to unavailability of fi->i_mmap_sem lock. So sleep - * only if fi->dax->nr=0 + * if a deadlock is possible if we sleep with + * mapping->invalidate_lock held and worker to free memory + * can't make progress due to unavailability of + * mapping->invalidate_lock. So sleep only if fi->dax->nr=0 */ if (retry) continue; @@ -1061,8 +1059,8 @@ alloc_dax_mapping_reclaim(struct fuse_conn_dax *fcd, struct inode *inode) * There are no mappings which can be reclaimed. Wait for one. * We are not holding fi->dax->sem. So it is possible * that range gets added now. But as we are not holding - * fi->i_mmap_sem, worker should still be able to free up - * a range and wake us up. + * mapping->invalidate_lock, worker should still be able to + * free up a range and wake us up. */ if (!fi->dax->nr && !(fcd->nr_free_ranges > 0)) { if (wait_event_killable_exclusive(fcd->range_waitq, @@ -1108,7 +1106,7 @@ static int lookup_and_reclaim_dmap_locked(struct fuse_conn_dax *fcd, /* * Free a range of memory. * Locking: - * 1. Take fi->i_mmap_sem to block dax faults. + * 1. Take mapping->invalidate_lock to block dax faults. * 2. Take fi->dax->sem to protect interval tree and also to make sure * read/write can not reuse a dmap which we might be freeing. */ @@ -1122,7 +1120,7 @@ static int lookup_and_reclaim_dmap(struct fuse_conn_dax *fcd, loff_t dmap_start = start_idx << FUSE_DAX_SHIFT; loff_t dmap_end = (dmap_start + FUSE_DAX_SZ) - 1; - down_write(&fi->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); ret = fuse_dax_break_layouts(inode, dmap_start, dmap_end); if (ret) { pr_debug("virtio_fs: fuse_dax_break_layouts() failed. err=%d\n", @@ -1134,7 +1132,7 @@ static int lookup_and_reclaim_dmap(struct fuse_conn_dax *fcd, ret = lookup_and_reclaim_dmap_locked(fcd, inode, start_idx); up_write(&fi->dax->sem); out_mmap_sem: - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); return ret; } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index eade6f965b2e..d9b977c0f38d 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1556,6 +1556,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, struct fuse_mount *fm = get_fuse_mount(inode); struct fuse_conn *fc = fm->fc; struct fuse_inode *fi = get_fuse_inode(inode); + struct address_space *mapping = inode->i_mapping; FUSE_ARGS(args); struct fuse_setattr_in inarg; struct fuse_attr_out outarg; @@ -1580,11 +1581,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, } if (FUSE_IS_DAX(inode) && is_truncate) { - down_write(&fi->i_mmap_sem); + filemap_invalidate_lock(mapping); fault_blocked = true; err = fuse_dax_break_layouts(inode, 0, 0); if (err) { - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(mapping); return err; } } @@ -1694,13 +1695,13 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, if ((is_truncate || !is_wb) && S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { truncate_pagecache(inode, outarg.attr.size); - invalidate_inode_pages2(inode->i_mapping); + invalidate_inode_pages2(mapping); } clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); out: if (fault_blocked) - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(mapping); return 0; @@ -1711,7 +1712,7 @@ error: clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); if (fault_blocked) - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(mapping); return err; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 97f860cfc195..621a662c19fb 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -243,7 +243,7 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) } if (dax_truncate) { - down_write(&get_fuse_inode(inode)->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); err = fuse_dax_break_layouts(inode, 0, 0); if (err) goto out; @@ -255,7 +255,7 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) out: if (dax_truncate) - up_write(&get_fuse_inode(inode)->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); if (is_wb_truncate | dax_truncate) { fuse_release_nowrite(inode); @@ -2920,7 +2920,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, if (lock_inode) { inode_lock(inode); if (block_faults) { - down_write(&fi->i_mmap_sem); + filemap_invalidate_lock(inode->i_mapping); err = fuse_dax_break_layouts(inode, 0, 0); if (err) goto out; @@ -2976,7 +2976,7 @@ out: clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); if (block_faults) - up_write(&fi->i_mmap_sem); + filemap_invalidate_unlock(inode->i_mapping); if (lock_inode) inode_unlock(inode); @@ -3045,7 +3045,7 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, * modifications. Yet this does give less guarantees than if the * copying was performed with write(2). * - * To fix this a i_mmap_sem style lock could be used to prevent new + * To fix this a mapping->invalidate_lock could be used to prevent new * faults while the copy is ongoing. */ err = fuse_writeback_range(inode_out, pos_out, pos_out + len - 1); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 07829ce78695..6fb639b97ea8 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -149,13 +149,6 @@ struct fuse_inode { /** Lock to protect write related fields */ spinlock_t lock; - /** - * Can't take inode lock in fault path (leads to circular dependency). - * Introduce another semaphore which can be taken in fault path and - * then other filesystem paths can take this to block faults. - */ - struct rw_semaphore i_mmap_sem; - #ifdef CONFIG_FUSE_DAX /* * Dax specific inode data diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index b9beb39a4a18..e07e429f32e1 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -85,7 +85,6 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->orig_ino = 0; fi->state = 0; mutex_init(&fi->mutex); - init_rwsem(&fi->i_mmap_sem); spin_lock_init(&fi->lock); fi->forget = fuse_alloc_forget(); if (!fi->forget) From 057ba5b24532aca202cb1ae8c246bde27de12763 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 22 Apr 2021 16:38:26 +0200 Subject: [PATCH 037/177] ceph: Fix race between hole punch and page fault Ceph has a following race between hole punching and page fault: CPU1 CPU2 ceph_fallocate() ... ceph_zero_pagecache_range() ceph_filemap_fault() faults in page in the range being punched ceph_zero_objects() And now we have a page in punched range with invalid data. Fix the problem by using mapping->invalidate_lock similarly to other filesystems. Note that using invalidate_lock also fixes a similar race wrt ->readpage(). CC: Jeff Layton CC: ceph-devel@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Jan Kara --- fs/ceph/addr.c | 9 ++++++--- fs/ceph/file.c | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index a1e2813731d1..7e7a897ae0d3 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -1395,9 +1395,11 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf) ret = VM_FAULT_SIGBUS; } else { struct address_space *mapping = inode->i_mapping; - struct page *page = find_or_create_page(mapping, 0, - mapping_gfp_constraint(mapping, - ~__GFP_FS)); + struct page *page; + + filemap_invalidate_lock_shared(mapping); + page = find_or_create_page(mapping, 0, + mapping_gfp_constraint(mapping, ~__GFP_FS)); if (!page) { ret = VM_FAULT_OOM; goto out_inline; @@ -1418,6 +1420,7 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf) vmf->page = page; ret = VM_FAULT_MAJOR | VM_FAULT_LOCKED; out_inline: + filemap_invalidate_unlock_shared(mapping); dout("filemap_fault %p %llu read inline data ret %x\n", inode, off, ret); } diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d1755ac1d964..e1d605a02d4a 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -2088,6 +2088,7 @@ static long ceph_fallocate(struct file *file, int mode, if (ret < 0) goto unlock; + filemap_invalidate_lock(inode->i_mapping); ceph_zero_pagecache_range(inode, offset, length); ret = ceph_zero_objects(inode, offset, length); @@ -2100,6 +2101,7 @@ static long ceph_fallocate(struct file *file, int mode, if (dirty) __mark_inode_dirty(inode, dirty); } + filemap_invalidate_unlock(inode->i_mapping); ceph_put_cap_refs(ci, got); unlock: From b092b3efc7cb239b6f33bb97da0f8812680e1046 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 22 Apr 2021 16:52:32 +0200 Subject: [PATCH 038/177] cifs: Fix race between hole punch and page fault Cifs has a following race between hole punching and page fault: CPU1 CPU2 smb3_fallocate() smb3_punch_hole() truncate_pagecache_range() filemap_fault() - loads old data into the page cache SMB2_ioctl(..., FSCTL_SET_ZERO_DATA, ...) And now we have stale data in the page cache. Fix the problem by locking out faults (as well as reads) using mapping->invalidate_lock while hole punch is running. CC: Steve French CC: linux-cifs@vger.kernel.org Signed-off-by: Jan Kara --- fs/cifs/smb2ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e4c8f603dd58..458c546ce8cd 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -3588,6 +3588,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, return rc; } + filemap_invalidate_lock(inode->i_mapping); /* * We implement the punch hole through ioctl, so we need remove the page * caches first, otherwise the data may be inconsistent with the server. @@ -3605,6 +3606,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, sizeof(struct file_zero_data_information), CIFSMaxBufSize, NULL, NULL); free_xid(xid); + filemap_invalidate_unlock(inode->i_mapping); return rc; } From e98fb032170bfa2e671a01e356a7ee86a2038312 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 14 Jul 2021 10:25:32 +0800 Subject: [PATCH 039/177] regulator: rtq6752: Refine binding document Drop regulators property reference and remove the status in example dts. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1626229532-13037-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- .../bindings/regulator/richtek,rtq6752-regulator.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml b/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml index 641840ea7c16..e6e5a9a7d940 100644 --- a/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/richtek,rtq6752-regulator.yaml @@ -30,7 +30,6 @@ properties: regulators: type: object - $ref: regulator.yaml# patternProperties: "^(p|n)avdd$": @@ -56,7 +55,6 @@ examples: rtq6752@6b { compatible = "richtek,rtq6752"; - status = "okay"; reg = <0x6b>; enable-gpios = <&gpio26 2 0>; From f84d866ab43fcc27b417c86357d6534f157a3d89 Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Tue, 13 Jul 2021 19:40:49 +0800 Subject: [PATCH 040/177] spi: mediatek: add tick_delay support This patch support tick_delay setting, some users need use high-speed spi speed, which can use tick_delay to tuning spi clk timing. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210713114048.29509-1-mason.zhang@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 11 ++++++++++- include/linux/platform_data/spi-mt65xx.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 097625d7915e..b34fbc913fd6 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -42,8 +42,9 @@ #define SPI_CFG1_CS_IDLE_OFFSET 0 #define SPI_CFG1_PACKET_LOOP_OFFSET 8 #define SPI_CFG1_PACKET_LENGTH_OFFSET 16 -#define SPI_CFG1_GET_TICK_DLY_OFFSET 30 +#define SPI_CFG1_GET_TICK_DLY_OFFSET 29 +#define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000 #define SPI_CFG1_CS_IDLE_MASK 0xff #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 @@ -152,6 +153,7 @@ static const struct mtk_spi_compatible mt6893_compat = { */ static const struct mtk_chip_config mtk_default_chip_info = { .sample_sel = 0, + .tick_delay = 0, }; static const struct of_device_id mtk_spi_of_match[] = { @@ -275,6 +277,13 @@ static int mtk_spi_prepare_message(struct spi_master *master, writel(mdata->pad_sel[spi->chip_select], mdata->base + SPI_PAD_SEL_REG); + /* tick delay */ + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; + reg_val |= ((chip_config->tick_delay & 0x7) + << SPI_CFG1_GET_TICK_DLY_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + return 0; } diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h index 65fd5ffd257c..f0db674f07b8 100644 --- a/include/linux/platform_data/spi-mt65xx.h +++ b/include/linux/platform_data/spi-mt65xx.h @@ -12,5 +12,6 @@ /* Board specific platform_data */ struct mtk_chip_config { u32 sample_sel; + u32 tick_delay; }; #endif From 014861c2fb3d7b38b8de32baa953082bb9dabaf4 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Mon, 21 Jun 2021 14:58:58 +0530 Subject: [PATCH 041/177] spi: omap-spi: Convert to json-schema Convert omap-spi dt-binding documentation from txt to yaml format. Signed-off-by: Aswath Govindraju Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210621092900.951-1-a-govindraju@ti.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/omap-spi.txt | 48 ------- .../devicetree/bindings/spi/omap-spi.yaml | 117 ++++++++++++++++++ 2 files changed, 117 insertions(+), 48 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/omap-spi.txt create mode 100644 Documentation/devicetree/bindings/spi/omap-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt deleted file mode 100644 index 487208c256c0..000000000000 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ /dev/null @@ -1,48 +0,0 @@ -OMAP2+ McSPI device - -Required properties: -- compatible : - - "ti,am654-mcspi" for AM654. - - "ti,omap2-mcspi" for OMAP2 & OMAP3. - - "ti,omap4-mcspi" for OMAP4+. -- ti,spi-num-cs : Number of chipselect supported by the instance. -- ti,hwmods: Name of the hwmod associated to the McSPI -- ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as - input. The default is D0 as input and - D1 as output. - -Optional properties: -- dmas: List of DMA specifiers with the controller specific format - as described in the generic DMA client binding. A tx and rx - specifier is required for each chip select. -- dma-names: List of DMA request names. These strings correspond - 1:1 with the DMA specifiers listed in dmas. The string naming - is to be "rxN" and "txN" for RX and TX requests, - respectively, where N equals the chip select number. - -Examples: - -[hwmod populated DMA resources] - -mcspi1: mcspi@1 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,omap4-mcspi"; - ti,hwmods = "mcspi1"; - ti,spi-num-cs = <4>; -}; - -[generic DMA request binding] - -mcspi1: mcspi@1 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,omap4-mcspi"; - ti,hwmods = "mcspi1"; - ti,spi-num-cs = <2>; - dmas = <&edma 42 - &edma 43 - &edma 44 - &edma 45>; - dma-names = "tx0", "rx0", "tx1", "rx1"; -}; diff --git a/Documentation/devicetree/bindings/spi/omap-spi.yaml b/Documentation/devicetree/bindings/spi/omap-spi.yaml new file mode 100644 index 000000000000..e55538186cf6 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/omap-spi.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/omap-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SPI controller bindings for OMAP and K3 SoCs + +maintainers: + - Aswath Govindraju + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - ti,am654-mcspi + - ti,am4372-mcspi + - const: ti,omap4-mcspi + - items: + - enum: + - ti,omap2-mcspi + - ti,omap4-mcspi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + ti,spi-num-cs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of chipselect supported by the instance. + minimum: 1 + maximum: 4 + + ti,hwmods: + $ref: /schemas/types.yaml#/definitions/string + description: + Must be "mcspi", n being the instance number (1-based). + This property is applicable only on legacy platforms mainly omap2/3 + and ti81xx and should not be used on other platforms. + deprecated: true + + ti,pindir-d0-out-d1-in: + description: + Select the D0 pin as output and D1 as input. The default is D0 + as input and D1 as output. + type: boolean + + dmas: + description: + List of DMA specifiers with the controller specific format as + described in the generic DMA client binding. A tx and rx + specifier is required for each chip select. + minItems: 1 + maxItems: 8 + + dma-names: + description: + List of DMA request names. These strings correspond 1:1 with + the DMA sepecifiers listed in dmas. The string names is to be + "rxN" and "txN" for RX and TX requests, respectively. Where N + is the chip select number. + minItems: 1 + maxItems: 8 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +if: + properties: + compatible: + oneOf: + - const: ti,omap2-mcspi + - const: ti,omap4-mcspi + +then: + properties: + ti,hwmods: + items: + - pattern: "^mcspi([1-9])$" + +else: + properties: + ti,hwmods: false + +examples: + - | + #include + #include + #include + + spi@2100000 { + compatible = "ti,am654-mcspi","ti,omap4-mcspi"; + reg = <0x2100000 0x400>; + interrupts = ; + clocks = <&k3_clks 137 1>; + power-domains = <&k3_pds 137 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <1>; + #size-cells = <0>; + dmas = <&main_udmap 0xc500>, <&main_udmap 0x4500>; + dma-names = "tx0", "rx0"; + }; From 77eac0e1ce9c61b66c15f7cc503ae848b74fe42c Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 14 Jul 2021 10:36:10 +0800 Subject: [PATCH 042/177] regulator: rtq6752: Fix the typo for reg define and author name Fix the typo for reg define and author name. Signed-off-by: ChiYuan Huang Reported-by: Axel Lin Link: https://lore.kernel.org/r/1626230170-13648-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/rtq6752-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/rtq6752-regulator.c b/drivers/regulator/rtq6752-regulator.c index fe31597983a6..72a72aa50d81 100644 --- a/drivers/regulator/rtq6752-regulator.c +++ b/drivers/regulator/rtq6752-regulator.c @@ -19,7 +19,7 @@ enum { #define RTQ6752_REG_PAVDD 0x00 #define RTQ6752_REG_NAVDD 0x01 #define RTQ6752_REG_PAVDDONDLY 0x07 -#define RTQ6752_REG_PAVDDSSTIME 0x07 +#define RTQ6752_REG_PAVDDSSTIME 0x08 #define RTQ6752_REG_NAVDDONDLY 0x0D #define RTQ6752_REG_NAVDDSSTIME 0x0E #define RTQ6752_REG_OPTION1 0x12 @@ -280,6 +280,6 @@ static struct i2c_driver rtq6752_driver = { }; module_i2c_driver(rtq6752_driver); -MODULE_AUTHOR("ChiYuan Hwang "); +MODULE_AUTHOR("ChiYuan Huang "); MODULE_DESCRIPTION("Richtek RTQ6752 Regulator Driver"); MODULE_LICENSE("GPL v2"); From 57f1c12e455fc6c4c0db2c9f14e57b95822c2321 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 12 Jul 2021 08:50:17 -0700 Subject: [PATCH 043/177] spi: spi-geni-qcom: Remove confusing comment about setting the watermark The comment in setup_fifo_xfer() about setting the watermark wasn't quite proper grammar and also stopped making sense around commit 6d66507d9b55 ("spi: spi-geni-qcom: Don't wait to start 1st transfer if transmitting"). After that commit we actually start the transfer _before_ the watermark interrupt comes. I don't think the comment really has any value anymore. We've already got a comment when we grab the spinlock saying that our interrupt can come any time as a result of the things in the locked section. Let's just remove it. Signed-off-by: Douglas Anderson Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20210712085010.1.Ie3bb9f9d30d6475bb75251d32635194c1c72b9ee@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index b3861fb88711..2f51421e2a71 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -549,12 +549,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, */ spin_lock_irq(&mas->lock); geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION); - - /* - * TX_WATERMARK_REG should be set after SPI configuration and - * setting up GENI SE engine, as driver starts data transfer - * for the watermark interrupt. - */ if (m_cmd & SPI_TX_ONLY) { if (geni_spi_handle_tx(mas)) writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); From 442a9d105e61591b36b653ba1ee0c02b0482b639 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 23 Jun 2021 10:52:17 +0900 Subject: [PATCH 044/177] regulator: Convert UniPhier regulator to json-schema Convert the UniPhier regulator binding to DT schema format. Signed-off-by: Kunihiko Hayashi Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1624413137-17453-1-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown --- .../socionext,uniphier-regulator.yaml | 85 +++++++++++++++++++ .../bindings/regulator/uniphier-regulator.txt | 58 ------------- 2 files changed, 85 insertions(+), 58 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/socionext,uniphier-regulator.yaml delete mode 100644 Documentation/devicetree/bindings/regulator/uniphier-regulator.txt diff --git a/Documentation/devicetree/bindings/regulator/socionext,uniphier-regulator.yaml b/Documentation/devicetree/bindings/regulator/socionext,uniphier-regulator.yaml new file mode 100644 index 000000000000..861d5f3c79e8 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/socionext,uniphier-regulator.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/socionext,uniphier-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext UniPhier regulator controller + +description: | + This regulator controls VBUS and belongs to USB3 glue layer. Before using + the regulator, it is necessary to control the clocks and resets to enable + this layer. These clocks and resets should be described in each property. + +maintainers: + - Kunihiko Hayashi + +allOf: + - $ref: "regulator.yaml#" + +# USB3 Controller + +properties: + compatible: + enum: + - socionext,uniphier-pro4-usb3-regulator + - socionext,uniphier-pro5-usb3-regulator + - socionext,uniphier-pxs2-usb3-regulator + - socionext,uniphier-ld20-usb3-regulator + - socionext,uniphier-pxs3-usb3-regulator + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + oneOf: + - items: # for Pro4, Pro5 + - const: gio + - const: link + - items: # for others + - const: link + + resets: + minItems: 1 + maxItems: 2 + + reset-names: + oneOf: + - items: # for Pro4, Pro5 + - const: gio + - const: link + - items: + - const: link + +additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + +examples: + - | + usb-glue@65b00000 { + compatible = "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x65b00000 0x400>; + + usb_vbus0: regulators@100 { + compatible = "socionext,uniphier-ld20-usb3-regulator"; + reg = <0x100 0x10>; + clock-names = "link"; + clocks = <&sys_clk 14>; + reset-names = "link"; + resets = <&sys_rst 14>; + }; + }; + diff --git a/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt b/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt deleted file mode 100644 index 94fd38b0d163..000000000000 --- a/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt +++ /dev/null @@ -1,58 +0,0 @@ -Socionext UniPhier Regulator Controller - -This describes the devicetree bindings for regulator controller implemented -on Socionext UniPhier SoCs. - -USB3 Controller ---------------- - -This regulator controls VBUS and belongs to USB3 glue layer. Before using -the regulator, it is necessary to control the clocks and resets to enable -this layer. These clocks and resets should be described in each property. - -Required properties: -- compatible: Should be - "socionext,uniphier-pro4-usb3-regulator" - for Pro4 SoC - "socionext,uniphier-pro5-usb3-regulator" - for Pro5 SoC - "socionext,uniphier-pxs2-usb3-regulator" - for PXs2 SoC - "socionext,uniphier-ld20-usb3-regulator" - for LD20 SoC - "socionext,uniphier-pxs3-usb3-regulator" - for PXs3 SoC -- reg: Specifies offset and length of the register set for the device. -- clocks: A list of phandles to the clock gate for USB3 glue layer. - According to the clock-names, appropriate clocks are required. -- clock-names: Should contain - "gio", "link" - for Pro4 and Pro5 SoCs - "link" - for others -- resets: A list of phandles to the reset control for USB3 glue layer. - According to the reset-names, appropriate resets are required. -- reset-names: Should contain - "gio", "link" - for Pro4 and Pro5 SoCs - "link" - for others - -See Documentation/devicetree/bindings/regulator/regulator.txt -for more details about the regulator properties. - -Example: - - usb-glue@65b00000 { - compatible = "socionext,uniphier-ld20-dwc3-glue", - "simple-mfd"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x65b00000 0x400>; - - usb_vbus0: regulators@100 { - compatible = "socionext,uniphier-ld20-usb3-regulator"; - reg = <0x100 0x10>; - clock-names = "link"; - clocks = <&sys_clk 14>; - reset-names = "link"; - resets = <&sys_rst 14>; - }; - - phy { - ... - phy-supply = <&usb_vbus0>; - }; - ... - }; From 508f8ccd993d1ff5c9a3092f179f33bd7a825bac Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 15 Jul 2021 15:15:31 +0100 Subject: [PATCH 045/177] regulator: rt6245: make a const array func_base static, makes object smaller Don't populate the const array func_base on the stack but instead it static. Makes the object code smaller by 55 bytes: Before: text data bss dec hex filename 6422 3216 64 9702 25e6 drivers/regulator/rt6245-regulator.o After: text data bss dec hex filename 6303 3280 64 9647 25af drivers/regulator/rt6245-regulator.o Reduction of 55 bytes (gcc version 10.3.0) Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20210715141531.27672-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/regulator/rt6245-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/rt6245-regulator.c b/drivers/regulator/rt6245-regulator.c index d3299a72fd10..cb22a207e9ff 100644 --- a/drivers/regulator/rt6245-regulator.c +++ b/drivers/regulator/rt6245-regulator.c @@ -144,7 +144,7 @@ static int rt6245_init_device_properties(struct device *dev) static int rt6245_reg_write(void *context, unsigned int reg, unsigned int val) { struct i2c_client *i2c = context; - const u8 func_base[] = { 0x6F, 0x73, 0x78, 0x61, 0x7C, 0 }; + static const u8 func_base[] = { 0x6F, 0x73, 0x78, 0x61, 0x7C, 0 }; unsigned int code, bit_count; code = func_base[reg]; From 541ee8f640327f951e7039278057827322231ab0 Mon Sep 17 00:00:00 2001 From: Vincent Pelletier Date: Tue, 13 Jul 2021 07:20:31 +0000 Subject: [PATCH 046/177] regulator: da9063: Add support for full-current mode. In addition to the ability of merging some power outputs, this chip has an overdrive mode. BCORE1, BCORE2 and BPRO have this ability, in which case the legal current draw is increased from 2 amps to 2.5 amps (at the expense of a quiescent current increase), and the configurable current limits are doubled. If a current higher than maximum half-current mode is requested, enable overdrive, and scale the current limit down. Symmetrically, scale the current limit up when querying a overdrive-enabled regulator. Signed-off-by: Vincent Pelletier Reviewed-by: Adam Thomson Link: https://lore.kernel.org/r/824518e6391b783a12eba9ff0527f06607a34bfb.1626160826.git.plr.vincent@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/da9063-regulator.c | 132 ++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index cf7d5341750e..82f52a2a031a 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -412,6 +412,134 @@ static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, return regmap_field_write(regl->suspend_sleep, val); } +static unsigned int da9063_get_overdrive_mask(const struct regulator_desc *desc) +{ + switch (desc->id) { + case DA9063_ID_BCORES_MERGED: + case DA9063_ID_BCORE1: + return DA9063_BCORE1_OD; + case DA9063_ID_BCORE2: + return DA9063_BCORE2_OD; + case DA9063_ID_BPRO: + return DA9063_BPRO_OD; + default: + return 0; + } +} + +static int da9063_buck_set_limit_set_overdrive(struct regulator_dev *rdev, + int min_uA, int max_uA, + unsigned int overdrive_mask) +{ + /* + * When enabling overdrive, do it before changing the current limit to + * ensure sufficient supply throughout the switch. + */ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + int ret; + unsigned int orig_overdrive; + + ret = regmap_read(regl->hw->regmap, DA9063_REG_CONFIG_H, + &orig_overdrive); + if (ret < 0) + return ret; + orig_overdrive &= overdrive_mask; + + if (orig_overdrive == 0) { + ret = regmap_set_bits(regl->hw->regmap, DA9063_REG_CONFIG_H, + overdrive_mask); + if (ret < 0) + return ret; + } + + ret = regulator_set_current_limit_regmap(rdev, min_uA / 2, max_uA / 2); + if (ret < 0 && orig_overdrive == 0) + /* + * regulator_set_current_limit_regmap may have rejected the + * change because of unusable min_uA and/or max_uA inputs. + * Attempt to restore original overdrive state, ignore failure- + * on-failure. + */ + regmap_clear_bits(regl->hw->regmap, DA9063_REG_CONFIG_H, + overdrive_mask); + + return ret; +} + +static int da9063_buck_set_limit_clear_overdrive(struct regulator_dev *rdev, + int min_uA, int max_uA, + unsigned int overdrive_mask) +{ + /* + * When disabling overdrive, do it after changing the current limit to + * ensure sufficient supply throughout the switch. + */ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + int ret, orig_limit; + + ret = regmap_read(rdev->regmap, rdev->desc->csel_reg, &orig_limit); + if (ret < 0) + return ret; + + ret = regulator_set_current_limit_regmap(rdev, min_uA, max_uA); + if (ret < 0) + return ret; + + ret = regmap_clear_bits(regl->hw->regmap, DA9063_REG_CONFIG_H, + overdrive_mask); + if (ret < 0) + /* + * Attempt to restore original current limit, ignore failure- + * on-failure. + */ + regmap_write(rdev->regmap, rdev->desc->csel_reg, orig_limit); + + return ret; +} + +static int da9063_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + unsigned int overdrive_mask, n_currents; + + overdrive_mask = da9063_get_overdrive_mask(rdev->desc); + if (overdrive_mask) { + n_currents = rdev->desc->n_current_limits; + if (n_currents == 0) + return -EINVAL; + + if (max_uA > rdev->desc->curr_table[n_currents - 1]) + return da9063_buck_set_limit_set_overdrive(rdev, min_uA, + max_uA, + overdrive_mask); + + return da9063_buck_set_limit_clear_overdrive(rdev, min_uA, + max_uA, + overdrive_mask); + } + return regulator_set_current_limit_regmap(rdev, min_uA, max_uA); +} + +static int da9063_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + int val, ret, limit; + unsigned int mask; + + limit = regulator_get_current_limit_regmap(rdev); + if (limit < 0) + return limit; + mask = da9063_get_overdrive_mask(rdev->desc); + if (mask) { + ret = regmap_read(regl->hw->regmap, DA9063_REG_CONFIG_H, &val); + if (ret < 0) + return ret; + if (val & mask) + limit *= 2; + } + return limit; +} + static const struct regulator_ops da9063_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -419,8 +547,8 @@ static const struct regulator_ops da9063_buck_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, - .set_current_limit = regulator_set_current_limit_regmap, - .get_current_limit = regulator_get_current_limit_regmap, + .set_current_limit = da9063_buck_set_current_limit, + .get_current_limit = da9063_buck_get_current_limit, .set_mode = da9063_buck_set_mode, .get_mode = da9063_buck_get_mode, .get_status = da9063_buck_get_status, From 513d14045a2dad0bf202b7d4c07a2ca2afdba0b4 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 16 Jul 2021 11:55:46 +0800 Subject: [PATCH 047/177] regulator: rtq6752: fix reg reset behavior Reg will be reset in below two conditions. 1. 'Enable' pin from H to L. 2. Both PAVDD and NAVDD are all disabled. And 'Enable' pin also control i2c communication capability. This patch is to Seperate the if condition in enable/disable callback for reg cache manipulation. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1626407746-23156-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/rtq6752-regulator.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/rtq6752-regulator.c b/drivers/regulator/rtq6752-regulator.c index 72a72aa50d81..609d3fcf4923 100644 --- a/drivers/regulator/rtq6752-regulator.c +++ b/drivers/regulator/rtq6752-regulator.c @@ -54,12 +54,14 @@ static int rtq6752_set_vdd_enable(struct regulator_dev *rdev) int rid = rdev_get_id(rdev), ret; mutex_lock(&priv->lock); - if (!priv->enable_flag && priv->enable_gpio) { + if (priv->enable_gpio) { gpiod_set_value(priv->enable_gpio, 1); usleep_range(RTQ6752_I2CRDY_TIMEUS, RTQ6752_I2CRDY_TIMEUS + 100); + } + if (!priv->enable_flag) { regcache_cache_only(priv->regmap, false); ret = regcache_sync(priv->regmap); if (ret) { @@ -86,12 +88,14 @@ static int rtq6752_set_vdd_disable(struct regulator_dev *rdev) mutex_lock(&priv->lock); priv->enable_flag &= ~BIT(rid); - if (!priv->enable_flag && priv->enable_gpio) { + if (!priv->enable_flag) { regcache_cache_only(priv->regmap, true); regcache_mark_dirty(priv->regmap); - - gpiod_set_value(priv->enable_gpio, 0); } + + if (priv->enable_gpio) + gpiod_set_value(priv->enable_gpio, 0); + mutex_unlock(&priv->lock); return 0; From f820547446ed05edee2944ebe19ea6a3104434f4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Jul 2021 17:27:05 +0200 Subject: [PATCH 048/177] power: supply: ab8500: Use library interpolation The kernel already has a static inline for linear interpolation so use that instead of rolling our own. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_btemp.c | 6 ++++-- drivers/power/supply/ab8500_fg.c | 14 +++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index dbdcff32f353..24958b935d39 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ab8500-bm.h" @@ -437,8 +438,9 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, i++; } - return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) * - (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); + return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp, + tbl[i + 1].resist, tbl[i + 1].temp, + res); } /** diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 3d45ed0157c6..bdbf3f13bee0 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "ab8500-bm.h" @@ -56,9 +57,6 @@ /* FG constants */ #define BATT_OVV 0x01 -#define interpolate(x, x1, y1, x2, y2) \ - ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1)))); - /** * struct ab8500_fg_interrupts - ab8500 fg interrupts * @name: name of the interrupt @@ -868,11 +866,12 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage) } if ((i > 0) && (i < tbl_size)) { - cap = interpolate(voltage, + cap = fixp_linear_interpolate( tbl[i].voltage, tbl[i].capacity * 10, tbl[i-1].voltage, - tbl[i-1].capacity * 10); + tbl[i-1].capacity * 10, + voltage); } else if (i == 0) { cap = 1000; } else { @@ -920,11 +919,12 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di) } if ((i > 0) && (i < tbl_size)) { - resist = interpolate(di->bat_temp / 10, + resist = fixp_linear_interpolate( tbl[i].temp, tbl[i].resist, tbl[i-1].temp, - tbl[i-1].resist); + tbl[i-1].resist, + di->bat_temp / 10); } else if (i == 0) { resist = tbl[0].resist; } else { From c5b64a990e7f3b0e3d9bf266b57384467fe382de Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Jul 2021 17:27:06 +0200 Subject: [PATCH 049/177] power: supply: ab8500: Rename charging algorithm symbols The "abx500" name on the charging algorithm stems from the ambition to produce a series of these analog basebands, re-using the same charging algorithm driver. No ASICs beside AB8500 and AB8505 were ever produced so this terminology is confusing. Rename the algorithm file and symbols to reflect the more narrow scope. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/Makefile | 2 +- drivers/power/supply/ab8500-bm.h | 2 +- .../{abx500_chargalg.c => ab8500_chargalg.c} | 576 +++++++++--------- drivers/power/supply/ab8500_charger.c | 2 +- 4 files changed, 291 insertions(+), 291 deletions(-) rename drivers/power/supply/{abx500_chargalg.c => ab8500_chargalg.c} (75%) diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 33059a91f60c..dde138bc1591 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o -obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h index 0c940571e5b0..4e417fbae60c 100644 --- a/drivers/power/supply/ab8500-bm.h +++ b/drivers/power/supply/ab8500-bm.h @@ -729,6 +729,6 @@ int ab8500_bm_of_probe(struct device *dev, extern struct platform_driver ab8500_fg_driver; extern struct platform_driver ab8500_btemp_driver; -extern struct platform_driver abx500_chargalg_driver; +extern struct platform_driver ab8500_chargalg_driver; #endif /* _AB8500_CHARGER_H_ */ diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c similarity index 75% rename from drivers/power/supply/abx500_chargalg.c rename to drivers/power/supply/ab8500_chargalg.c index a17849bfacbf..b0bbb1c4b83a 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -3,7 +3,7 @@ * Copyright (C) ST-Ericsson SA 2012 * Copyright (c) 2012 Sony Mobile Communications AB * - * Charging algorithm driver for abx500 variants + * Charging algorithm driver for AB8500 * * Authors: * Johan Palsson @@ -49,18 +49,18 @@ #define CHARGALG_CURR_STEP_LOW 0 #define CHARGALG_CURR_STEP_HIGH 100 -enum abx500_chargers { +enum ab8500_chargers { NO_CHG, AC_CHG, USB_CHG, }; -struct abx500_chargalg_charger_info { - enum abx500_chargers conn_chg; - enum abx500_chargers prev_conn_chg; - enum abx500_chargers online_chg; - enum abx500_chargers prev_online_chg; - enum abx500_chargers charger_type; +struct ab8500_chargalg_charger_info { + enum ab8500_chargers conn_chg; + enum ab8500_chargers prev_conn_chg; + enum ab8500_chargers online_chg; + enum ab8500_chargers prev_online_chg; + enum ab8500_chargers charger_type; bool usb_chg_ok; bool ac_chg_ok; int usb_volt; @@ -73,18 +73,18 @@ struct abx500_chargalg_charger_info { int ac_iset; }; -struct abx500_chargalg_suspension_status { +struct ab8500_chargalg_suspension_status { bool suspended_change; bool ac_suspended; bool usb_suspended; }; -struct abx500_chargalg_current_step_status { +struct ab8500_chargalg_current_step_status { bool curr_step_change; int curr_step; }; -struct abx500_chargalg_battery_data { +struct ab8500_chargalg_battery_data { int temp; int volt; int avg_curr; @@ -92,7 +92,7 @@ struct abx500_chargalg_battery_data { int percent; }; -enum abx500_chargalg_states { +enum ab8500_chargalg_states { STATE_HANDHELD_INIT, STATE_HANDHELD, STATE_CHG_NOT_OK_INIT, @@ -154,7 +154,7 @@ static const char *states[] = { "WD_EXPIRED", }; -struct abx500_chargalg_events { +struct ab8500_chargalg_events { bool batt_unknown; bool mainextchnotok; bool batt_ovv; @@ -176,7 +176,7 @@ struct abx500_chargalg_events { }; /** - * struct abx500_charge_curr_maximization - Charger maximization parameters + * struct ab8500_charge_curr_maximization - Charger maximization parameters * @original_iset: the non optimized/maximised charger current * @current_iset: the charging current used at this moment * @test_delta_i: the delta between the current we want to charge and the @@ -190,7 +190,7 @@ struct abx500_chargalg_events { * @level: tells in how many steps the charging current has been increased */ -struct abx500_charge_curr_maximization { +struct ab8500_charge_curr_maximization { int original_iset; int current_iset; int test_delta_i; @@ -207,7 +207,7 @@ enum maxim_ret { }; /** - * struct abx500_chargalg - abx500 Charging algorithm device information + * struct ab8500_chargalg - ab8500 Charging algorithm device information * @dev: pointer to the structure device * @charge_status: battery operating status * @eoc_cnt: counter used to determine end-of_charge @@ -223,7 +223,7 @@ enum maxim_ret { * @susp_status: current charger suspension status * @bm: Platform specific battery management information * @curr_status: Current step status for over-current protection - * @parent: pointer to the struct abx500 + * @parent: pointer to the struct ab8500 * @chargalg_psy: structure that holds the battery properties exposed by * the charging algorithm * @events: structure for information about events triggered @@ -235,25 +235,25 @@ enum maxim_ret { * @maintenance_timer: maintenance charging timer * @chargalg_kobject: structure of type kobject */ -struct abx500_chargalg { +struct ab8500_chargalg { struct device *dev; int charge_status; int eoc_cnt; bool maintenance_chg; int t_hyst_norm; int t_hyst_lowhigh; - enum abx500_chargalg_states charge_state; - struct abx500_charge_curr_maximization ccm; - struct abx500_chargalg_charger_info chg_info; - struct abx500_chargalg_battery_data batt_data; - struct abx500_chargalg_suspension_status susp_status; + enum ab8500_chargalg_states charge_state; + struct ab8500_charge_curr_maximization ccm; + struct ab8500_chargalg_charger_info chg_info; + struct ab8500_chargalg_battery_data batt_data; + struct ab8500_chargalg_suspension_status susp_status; struct ab8500 *parent; - struct abx500_chargalg_current_step_status curr_status; + struct ab8500_chargalg_current_step_status curr_status; struct abx500_bm_data *bm; struct power_supply *chargalg_psy; struct ux500_charger *ac_chg; struct ux500_charger *usb_chg; - struct abx500_chargalg_events events; + struct ab8500_chargalg_events events; struct workqueue_struct *chargalg_wq; struct delayed_work chargalg_periodic_work; struct delayed_work chargalg_wd_work; @@ -267,28 +267,28 @@ struct abx500_chargalg { BLOCKING_NOTIFIER_HEAD(charger_notifier_list); /* Main battery properties */ -static enum power_supply_property abx500_chargalg_props[] = { +static enum power_supply_property ab8500_chargalg_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, }; -struct abx500_chargalg_sysfs_entry { +struct ab8500_chargalg_sysfs_entry { struct attribute attr; - ssize_t (*show)(struct abx500_chargalg *, char *); - ssize_t (*store)(struct abx500_chargalg *, const char *, size_t); + ssize_t (*show)(struct ab8500_chargalg *, char *); + ssize_t (*store)(struct ab8500_chargalg *, const char *, size_t); }; /** - * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer + * ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer * @timer: pointer to the hrtimer structure * * This function gets called when the safety timer for the charger * expires */ static enum hrtimer_restart -abx500_chargalg_safety_timer_expired(struct hrtimer *timer) +ab8500_chargalg_safety_timer_expired(struct hrtimer *timer) { - struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, + struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, safety_timer); dev_err(di->dev, "Safety timer expired\n"); di->events.safety_timer_expired = true; @@ -300,7 +300,7 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer) } /** - * abx500_chargalg_maintenance_timer_expired() - Expiration of + * ab8500_chargalg_maintenance_timer_expired() - Expiration of * the maintenance timer * @timer: pointer to the timer structure * @@ -308,10 +308,10 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer) * expires */ static enum hrtimer_restart -abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer) +ab8500_chargalg_maintenance_timer_expired(struct hrtimer *timer) { - struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, + struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, maintenance_timer); dev_dbg(di->dev, "Maintenance timer expired\n"); @@ -324,13 +324,13 @@ abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer) } /** - * abx500_chargalg_state_to() - Change charge state - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_state_to() - Change charge state + * @di: pointer to the ab8500_chargalg structure * * This function gets called when a charge state change should occur */ -static void abx500_chargalg_state_to(struct abx500_chargalg *di, - enum abx500_chargalg_states state) +static void ab8500_chargalg_state_to(struct ab8500_chargalg *di, + enum ab8500_chargalg_states state) { dev_dbg(di->dev, "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n", @@ -343,7 +343,7 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di, di->charge_state = state; } -static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) +static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di) { switch (di->charge_state) { case STATE_NORMAL: @@ -368,13 +368,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) } /** - * abx500_chargalg_check_charger_connection() - Check charger connection change - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_check_charger_connection() - Check charger connection change + * @di: pointer to the ab8500_chargalg structure * * This function will check if there is a change in the charger connection * and change charge state accordingly. AC has precedence over USB. */ -static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) +static int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di) { if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg || di->susp_status.suspended_change) { @@ -387,23 +387,23 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) dev_dbg(di->dev, "Charging source is AC\n"); if (di->chg_info.charger_type != AC_CHG) { di->chg_info.charger_type = AC_CHG; - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); } } else if ((di->chg_info.conn_chg & USB_CHG) && !di->susp_status.usb_suspended) { dev_dbg(di->dev, "Charging source is USB\n"); di->chg_info.charger_type = USB_CHG; - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); } else if (di->chg_info.conn_chg && (di->susp_status.ac_suspended || di->susp_status.usb_suspended)) { dev_dbg(di->dev, "Charging is suspended\n"); di->chg_info.charger_type = NO_CHG; - abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT); + ab8500_chargalg_state_to(di, STATE_SUSPENDED_INIT); } else { dev_dbg(di->dev, "Charging source is OFF\n"); di->chg_info.charger_type = NO_CHG; - abx500_chargalg_state_to(di, STATE_HANDHELD_INIT); + ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); } di->chg_info.prev_conn_chg = di->chg_info.conn_chg; di->susp_status.suspended_change = false; @@ -412,29 +412,29 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) } /** - * abx500_chargalg_check_current_step_status() - Check charging current + * ab8500_chargalg_check_current_step_status() - Check charging current * step status. - * @di: pointer to the abx500_chargalg structure + * @di: pointer to the ab8500_chargalg structure * * This function will check if there is a change in the charging current step * and change charge state accordingly. */ -static void abx500_chargalg_check_current_step_status - (struct abx500_chargalg *di) +static void ab8500_chargalg_check_current_step_status + (struct ab8500_chargalg *di) { if (di->curr_status.curr_step_change) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); di->curr_status.curr_step_change = false; } /** - * abx500_chargalg_start_safety_timer() - Start charging safety timer - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_start_safety_timer() - Start charging safety timer + * @di: pointer to the ab8500_chargalg structure * * The safety timer is used to avoid overcharging of old or bad batteries. * There are different timers for AC and USB */ -static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) +static void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di) { /* Charger-dependent expiration time in hours*/ int timer_expiration = 0; @@ -461,27 +461,27 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) } /** - * abx500_chargalg_stop_safety_timer() - Stop charging safety timer - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_stop_safety_timer() - Stop charging safety timer + * @di: pointer to the ab8500_chargalg structure * * The safety timer is stopped whenever the NORMAL state is exited */ -static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) +static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di) { if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) di->events.safety_timer_expired = false; } /** - * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer + * @di: pointer to the ab8500_chargalg structure * @duration: duration of ther maintenance timer in hours * * The maintenance timer is used to maintain the charge in the battery once * the battery is considered full. These timers are chosen to match the * discharge curve of the battery */ -static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, +static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di, int duration) { hrtimer_set_expires_range(&di->maintenance_timer, @@ -492,26 +492,26 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, } /** - * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer + * @di: pointer to the ab8500_chargalg structure * * The maintenance timer is stopped whenever maintenance ends or when another * state is entered */ -static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di) +static void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di) { if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) di->events.maintenance_timer_expired = false; } /** - * abx500_chargalg_kick_watchdog() - Kick charger watchdog - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_kick_watchdog() - Kick charger watchdog + * @di: pointer to the ab8500_chargalg structure * * The charger watchdog have to be kicked periodically whenever the charger is * on, else the ABB will reset the system */ -static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) +static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) { /* Check if charger exists and kick watchdog if charging */ if (di->ac_chg && di->ac_chg->ops.kick_wd && @@ -535,8 +535,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) } /** - * abx500_chargalg_ac_en() - Turn on/off the AC charger - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_ac_en() - Turn on/off the AC charger + * @di: pointer to the ab8500_chargalg structure * @enable: charger on/off * @vset: requested charger output voltage * @iset: requested charger output current @@ -544,10 +544,10 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) * The AC charger will be turned on/off with the requested charge voltage and * current */ -static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, +static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable, int vset, int iset) { - static int abx500_chargalg_ex_ac_enable_toggle; + static int ab8500_chargalg_ex_ac_enable_toggle; if (!di->ac_chg || !di->ac_chg->ops.enable) return -ENXIO; @@ -563,18 +563,18 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, /* Enable external charger */ if (enable && di->ac_chg->external && - !abx500_chargalg_ex_ac_enable_toggle) { + !ab8500_chargalg_ex_ac_enable_toggle) { blocking_notifier_call_chain(&charger_notifier_list, 0, di->dev); - abx500_chargalg_ex_ac_enable_toggle++; + ab8500_chargalg_ex_ac_enable_toggle++; } return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset); } /** - * abx500_chargalg_usb_en() - Turn on/off the USB charger - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_usb_en() - Turn on/off the USB charger + * @di: pointer to the ab8500_chargalg structure * @enable: charger on/off * @vset: requested charger output voltage * @iset: requested charger output current @@ -582,7 +582,7 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, * The USB charger will be turned on/off with the requested charge voltage and * current */ -static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable, +static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable, int vset, int iset) { if (!di->usb_chg || !di->usb_chg->ops.enable) @@ -601,14 +601,14 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable, } /** - * abx500_chargalg_update_chg_curr() - Update charger current - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_update_chg_curr() - Update charger current + * @di: pointer to the ab8500_chargalg structure * @iset: requested charger output current * * The charger output current will be updated for the charger * that is currently in use */ -static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di, +static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di, int iset) { /* Check if charger exists and update current if charging */ @@ -642,19 +642,19 @@ static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di, } /** - * abx500_chargalg_stop_charging() - Stop charging - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_stop_charging() - Stop charging + * @di: pointer to the ab8500_chargalg structure * * This function is called from any state where charging should be stopped. * All charging is disabled and all status parameters and timers are changed * accordingly */ -static void abx500_chargalg_stop_charging(struct abx500_chargalg *di) +static void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di) { - abx500_chargalg_ac_en(di, false, 0, 0); - abx500_chargalg_usb_en(di, false, 0, 0); - abx500_chargalg_stop_safety_timer(di); - abx500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_ac_en(di, false, 0, 0); + ab8500_chargalg_usb_en(di, false, 0, 0); + ab8500_chargalg_stop_safety_timer(di); + ab8500_chargalg_stop_maintenance_timer(di); di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; di->maintenance_chg = false; cancel_delayed_work(&di->chargalg_wd_work); @@ -662,19 +662,19 @@ static void abx500_chargalg_stop_charging(struct abx500_chargalg *di) } /** - * abx500_chargalg_hold_charging() - Pauses charging - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_hold_charging() - Pauses charging + * @di: pointer to the ab8500_chargalg structure * * This function is called in the case where maintenance charging has been * disabled and instead a battery voltage mode is entered to check when the * battery voltage has reached a certain recharge voltage */ -static void abx500_chargalg_hold_charging(struct abx500_chargalg *di) +static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di) { - abx500_chargalg_ac_en(di, false, 0, 0); - abx500_chargalg_usb_en(di, false, 0, 0); - abx500_chargalg_stop_safety_timer(di); - abx500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_ac_en(di, false, 0, 0); + ab8500_chargalg_usb_en(di, false, 0, 0); + ab8500_chargalg_stop_safety_timer(di); + ab8500_chargalg_stop_maintenance_timer(di); di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->maintenance_chg = false; cancel_delayed_work(&di->chargalg_wd_work); @@ -682,30 +682,30 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di) } /** - * abx500_chargalg_start_charging() - Start the charger - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_start_charging() - Start the charger + * @di: pointer to the ab8500_chargalg structure * @vset: requested charger output voltage * @iset: requested charger output current * * A charger will be enabled depending on the requested charger type that was * detected previously. */ -static void abx500_chargalg_start_charging(struct abx500_chargalg *di, +static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di, int vset, int iset) { switch (di->chg_info.charger_type) { case AC_CHG: dev_dbg(di->dev, "AC parameters: Vset %d, Ich %d\n", vset, iset); - abx500_chargalg_usb_en(di, false, 0, 0); - abx500_chargalg_ac_en(di, true, vset, iset); + ab8500_chargalg_usb_en(di, false, 0, 0); + ab8500_chargalg_ac_en(di, true, vset, iset); break; case USB_CHG: dev_dbg(di->dev, "USB parameters: Vset %d, Ich %d\n", vset, iset); - abx500_chargalg_ac_en(di, false, 0, 0); - abx500_chargalg_usb_en(di, true, vset, iset); + ab8500_chargalg_ac_en(di, false, 0, 0); + ab8500_chargalg_usb_en(di, true, vset, iset); break; default: @@ -715,13 +715,13 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di, } /** - * abx500_chargalg_check_temp() - Check battery temperature ranges - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_check_temp() - Check battery temperature ranges + * @di: pointer to the ab8500_chargalg structure * * The battery temperature is checked against the predefined limits and the * charge state is changed accordingly */ -static void abx500_chargalg_check_temp(struct abx500_chargalg *di) +static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di) { if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) && di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) { @@ -760,12 +760,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di) } /** - * abx500_chargalg_check_charger_voltage() - Check charger voltage - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_check_charger_voltage() - Check charger voltage + * @di: pointer to the ab8500_chargalg structure * * Charger voltage is checked against maximum limit */ -static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di) +static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di) { if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max) di->chg_info.usb_chg_ok = false; @@ -780,14 +780,14 @@ static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di) } /** - * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled + * @di: pointer to the ab8500_chargalg structure * * End-of-charge criteria is fulfilled when the battery voltage is above a * certain limit and the battery current is below a certain limit for a * predefined number of consecutive seconds. If true, the battery is full */ -static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) +static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di) { if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING && di->charge_state == STATE_NORMAL && @@ -815,7 +815,7 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) } } -static void init_maxim_chg_curr(struct abx500_chargalg *di) +static void init_maxim_chg_curr(struct ab8500_chargalg *di) { di->ccm.original_iset = di->bm->bat_type[di->bm->batt_id].normal_cur_lvl; @@ -828,15 +828,15 @@ static void init_maxim_chg_curr(struct abx500_chargalg *di) } /** - * abx500_chargalg_chg_curr_maxim - increases the charger current to + * ab8500_chargalg_chg_curr_maxim - increases the charger current to * compensate for the system load - * @di pointer to the abx500_chargalg structure + * @di pointer to the ab8500_chargalg structure * * This maximization function is used to raise the charger current to get the * battery current as close to the optimal value as possible. The battery * current during charging is affected by the system load */ -static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) +static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di) { int delta_i; @@ -908,21 +908,21 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) } } -static void handle_maxim_chg_curr(struct abx500_chargalg *di) +static void handle_maxim_chg_curr(struct ab8500_chargalg *di) { enum maxim_ret ret; int result; - ret = abx500_chargalg_chg_curr_maxim(di); + ret = ab8500_chargalg_chg_curr_maxim(di); switch (ret) { case MAXIM_RET_CHANGE: - result = abx500_chargalg_update_chg_curr(di, + result = ab8500_chargalg_update_chg_curr(di, di->ccm.current_iset); if (result) dev_err(di->dev, "failed to set chg curr\n"); break; case MAXIM_RET_IBAT_TOO_HIGH: - result = abx500_chargalg_update_chg_curr(di, + result = ab8500_chargalg_update_chg_curr(di, di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); if (result) dev_err(di->dev, "failed to set chg curr\n"); @@ -935,12 +935,12 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di) } } -static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) +static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data) { struct power_supply *psy; struct power_supply *ext = dev_get_drvdata(dev); const char **supplicants = (const char **)ext->supplied_to; - struct abx500_chargalg *di; + struct ab8500_chargalg *di; union power_supply_propval ret; int j; bool capacity_updated = false; @@ -1259,7 +1259,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) } /** - * abx500_chargalg_external_power_changed() - callback for power supply changes + * ab8500_chargalg_external_power_changed() - callback for power supply changes * @psy: pointer to the structure power_supply * * This function is the entry point of the pointer external_power_changed @@ -1267,9 +1267,9 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) * This function gets executed when there is a change in any external power * supply that this driver needs to be notified of. */ -static void abx500_chargalg_external_power_changed(struct power_supply *psy) +static void ab8500_chargalg_external_power_changed(struct power_supply *psy) { - struct abx500_chargalg *di = power_supply_get_drvdata(psy); + struct ab8500_chargalg *di = power_supply_get_drvdata(psy); /* * Trigger execution of the algorithm instantly and read @@ -1279,14 +1279,14 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy) } /** - * abx500_chargalg_algorithm() - Main function for the algorithm - * @di: pointer to the abx500_chargalg structure + * ab8500_chargalg_algorithm() - Main function for the algorithm + * @di: pointer to the ab8500_chargalg structure * * This is the main control function for the charging algorithm. * It is called periodically or when something happens that will * trigger a state change */ -static void abx500_chargalg_algorithm(struct abx500_chargalg *di) +static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) { int charger_status; int ret; @@ -1294,17 +1294,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) /* Collect data from all power_supply class devices */ class_for_each_device(power_supply_class, NULL, - di->chargalg_psy, abx500_chargalg_get_ext_psy_data); + di->chargalg_psy, ab8500_chargalg_get_ext_psy_data); - abx500_chargalg_end_of_charge(di); - abx500_chargalg_check_temp(di); - abx500_chargalg_check_charger_voltage(di); + ab8500_chargalg_end_of_charge(di); + ab8500_chargalg_check_temp(di); + ab8500_chargalg_check_charger_voltage(di); - charger_status = abx500_chargalg_check_charger_connection(di); - abx500_chargalg_check_current_step_status(di); + charger_status = ab8500_chargalg_check_charger_connection(di); + ab8500_chargalg_check_current_step_status(di); if (is_ab8500(di->parent)) { - ret = abx500_chargalg_check_charger_enable(di); + ret = ab8500_chargalg_check_charger_enable(di); if (ret < 0) dev_err(di->dev, "Checking charger is enabled error" ": Returned Value %d\n", ret); @@ -1319,7 +1319,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) (di->events.batt_unknown && !di->bm->chg_unknown_bat)) { if (di->charge_state != STATE_HANDHELD) { di->events.safety_timer_expired = false; - abx500_chargalg_state_to(di, STATE_HANDHELD_INIT); + ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); } } @@ -1332,7 +1332,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) /* Safety timer expiration */ else if (di->events.safety_timer_expired) { if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED) - abx500_chargalg_state_to(di, + ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED_INIT); } /* @@ -1343,7 +1343,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) /* Battery removed */ else if (di->events.batt_rem) { if (di->charge_state != STATE_BATT_REMOVED) - abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT); + ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT); } /* Main or USB charger not ok. */ else if (di->events.mainextchnotok || di->events.usbchargernotok) { @@ -1353,7 +1353,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) */ if (di->charge_state != STATE_CHG_NOT_OK && !di->events.vbus_collapsed) - abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT); + ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT); } /* VBUS, Main or VBAT OVV. */ else if (di->events.vbus_ovv || @@ -1362,31 +1362,31 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) !di->chg_info.usb_chg_ok || !di->chg_info.ac_chg_ok) { if (di->charge_state != STATE_OVV_PROTECT) - abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT); + ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT); } /* USB Thermal, stop charging */ else if (di->events.main_thermal_prot || di->events.usb_thermal_prot) { if (di->charge_state != STATE_HW_TEMP_PROTECT) - abx500_chargalg_state_to(di, + ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT_INIT); } /* Battery temp over/under */ else if (di->events.btemp_underover) { if (di->charge_state != STATE_TEMP_UNDEROVER) - abx500_chargalg_state_to(di, + ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER_INIT); } /* Watchdog expired */ else if (di->events.ac_wd_expired || di->events.usb_wd_expired) { if (di->charge_state != STATE_WD_EXPIRED) - abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT); + ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT); } /* Battery temp high/low */ else if (di->events.btemp_lowhigh) { if (di->charge_state != STATE_TEMP_LOWHIGH) - abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT); + ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT); } dev_dbg(di->dev, @@ -1418,9 +1418,9 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) switch (di->charge_state) { case STATE_HANDHELD_INIT: - abx500_chargalg_stop_charging(di); + ab8500_chargalg_stop_charging(di); di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; - abx500_chargalg_state_to(di, STATE_HANDHELD); + ab8500_chargalg_state_to(di, STATE_HANDHELD); fallthrough; case STATE_HANDHELD: @@ -1428,14 +1428,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) case STATE_SUSPENDED_INIT: if (di->susp_status.ac_suspended) - abx500_chargalg_ac_en(di, false, 0, 0); + ab8500_chargalg_ac_en(di, false, 0, 0); if (di->susp_status.usb_suspended) - abx500_chargalg_usb_en(di, false, 0, 0); - abx500_chargalg_stop_safety_timer(di); - abx500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_usb_en(di, false, 0, 0); + ab8500_chargalg_stop_safety_timer(di); + ab8500_chargalg_stop_maintenance_timer(di); di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; di->maintenance_chg = false; - abx500_chargalg_state_to(di, STATE_SUSPENDED); + ab8500_chargalg_state_to(di, STATE_SUSPENDED); power_supply_changed(di->chargalg_psy); fallthrough; @@ -1444,29 +1444,29 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) break; case STATE_BATT_REMOVED_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_BATT_REMOVED); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_BATT_REMOVED); fallthrough; case STATE_BATT_REMOVED: if (!di->events.batt_rem) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_HW_TEMP_PROTECT_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT); fallthrough; case STATE_HW_TEMP_PROTECT: if (!di->events.main_thermal_prot && !di->events.usb_thermal_prot) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_OVV_PROTECT_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_OVV_PROTECT); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_OVV_PROTECT); fallthrough; case STATE_OVV_PROTECT: @@ -1475,23 +1475,23 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) !di->events.batt_ovv && di->chg_info.usb_chg_ok && di->chg_info.ac_chg_ok) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_CHG_NOT_OK_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_CHG_NOT_OK); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK); fallthrough; case STATE_CHG_NOT_OK: if (!di->events.mainextchnotok && !di->events.usbchargernotok) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_SAFETY_TIMER_EXPIRED_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED); fallthrough; case STATE_SAFETY_TIMER_EXPIRED: @@ -1500,20 +1500,20 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) case STATE_NORMAL_INIT: if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW) - abx500_chargalg_stop_charging(di); + ab8500_chargalg_stop_charging(di); else { curr_step_lvl = di->bm->bat_type[ di->bm->batt_id].normal_cur_lvl * di->curr_status.curr_step / CHARGALG_CURR_STEP_HIGH; - abx500_chargalg_start_charging(di, + ab8500_chargalg_start_charging(di, di->bm->bat_type[di->bm->batt_id] .normal_vol_lvl, curr_step_lvl); } - abx500_chargalg_state_to(di, STATE_NORMAL); - abx500_chargalg_start_safety_timer(di); - abx500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_state_to(di, STATE_NORMAL); + ab8500_chargalg_start_safety_timer(di); + ab8500_chargalg_stop_maintenance_timer(di); init_maxim_chg_curr(di); di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->eoc_cnt = 0; @@ -1527,104 +1527,104 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) if (di->charge_status == POWER_SUPPLY_STATUS_FULL && di->maintenance_chg) { if (di->bm->no_maintenance) - abx500_chargalg_state_to(di, + ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE_INIT); else - abx500_chargalg_state_to(di, + ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A_INIT); } break; /* This state will be used when the maintenance state is disabled */ case STATE_WAIT_FOR_RECHARGE_INIT: - abx500_chargalg_hold_charging(di); - abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); + ab8500_chargalg_hold_charging(di); + ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); fallthrough; case STATE_WAIT_FOR_RECHARGE: if (di->batt_data.percent <= di->bm->bat_type[di->bm->batt_id]. recharge_cap) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_MAINTENANCE_A_INIT: - abx500_chargalg_stop_safety_timer(di); - abx500_chargalg_start_maintenance_timer(di, + ab8500_chargalg_stop_safety_timer(di); + ab8500_chargalg_start_maintenance_timer(di, di->bm->bat_type[ di->bm->batt_id].maint_a_chg_timer_h); - abx500_chargalg_start_charging(di, + ab8500_chargalg_start_charging(di, di->bm->bat_type[ di->bm->batt_id].maint_a_vol_lvl, di->bm->bat_type[ di->bm->batt_id].maint_a_cur_lvl); - abx500_chargalg_state_to(di, STATE_MAINTENANCE_A); + ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A); power_supply_changed(di->chargalg_psy); fallthrough; case STATE_MAINTENANCE_A: if (di->events.maintenance_timer_expired) { - abx500_chargalg_stop_maintenance_timer(di); - abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); + ab8500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); } break; case STATE_MAINTENANCE_B_INIT: - abx500_chargalg_start_maintenance_timer(di, + ab8500_chargalg_start_maintenance_timer(di, di->bm->bat_type[ di->bm->batt_id].maint_b_chg_timer_h); - abx500_chargalg_start_charging(di, + ab8500_chargalg_start_charging(di, di->bm->bat_type[ di->bm->batt_id].maint_b_vol_lvl, di->bm->bat_type[ di->bm->batt_id].maint_b_cur_lvl); - abx500_chargalg_state_to(di, STATE_MAINTENANCE_B); + ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B); power_supply_changed(di->chargalg_psy); fallthrough; case STATE_MAINTENANCE_B: if (di->events.maintenance_timer_expired) { - abx500_chargalg_stop_maintenance_timer(di); - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); } break; case STATE_TEMP_LOWHIGH_INIT: - abx500_chargalg_start_charging(di, + ab8500_chargalg_start_charging(di, di->bm->bat_type[ di->bm->batt_id].low_high_vol_lvl, di->bm->bat_type[ di->bm->batt_id].low_high_cur_lvl); - abx500_chargalg_stop_maintenance_timer(di); + ab8500_chargalg_stop_maintenance_timer(di); di->charge_status = POWER_SUPPLY_STATUS_CHARGING; - abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); + ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); power_supply_changed(di->chargalg_psy); fallthrough; case STATE_TEMP_LOWHIGH: if (!di->events.btemp_lowhigh) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_WD_EXPIRED_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_WD_EXPIRED); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_WD_EXPIRED); fallthrough; case STATE_WD_EXPIRED: if (!di->events.ac_wd_expired && !di->events.usb_wd_expired) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; case STATE_TEMP_UNDEROVER_INIT: - abx500_chargalg_stop_charging(di); - abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER); + ab8500_chargalg_stop_charging(di); + ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER); fallthrough; case STATE_TEMP_UNDEROVER: if (!di->events.btemp_underover) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; } @@ -1636,17 +1636,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) } /** - * abx500_chargalg_periodic_work() - Periodic work for the algorithm + * ab8500_chargalg_periodic_work() - Periodic work for the algorithm * @work: pointer to the work_struct structure * * Work queue function for the charging algorithm */ -static void abx500_chargalg_periodic_work(struct work_struct *work) +static void ab8500_chargalg_periodic_work(struct work_struct *work) { - struct abx500_chargalg *di = container_of(work, - struct abx500_chargalg, chargalg_periodic_work.work); + struct ab8500_chargalg *di = container_of(work, + struct ab8500_chargalg, chargalg_periodic_work.work); - abx500_chargalg_algorithm(di); + ab8500_chargalg_algorithm(di); /* * If a charger is connected then the battery has to be monitored @@ -1663,20 +1663,20 @@ static void abx500_chargalg_periodic_work(struct work_struct *work) } /** - * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog + * ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog * @work: pointer to the work_struct structure * * Work queue function for kicking the charger watchdog */ -static void abx500_chargalg_wd_work(struct work_struct *work) +static void ab8500_chargalg_wd_work(struct work_struct *work) { int ret; - struct abx500_chargalg *di = container_of(work, - struct abx500_chargalg, chargalg_wd_work.work); + struct ab8500_chargalg *di = container_of(work, + struct ab8500_chargalg, chargalg_wd_work.work); - dev_dbg(di->dev, "abx500_chargalg_wd_work\n"); + dev_dbg(di->dev, "ab8500_chargalg_wd_work\n"); - ret = abx500_chargalg_kick_watchdog(di); + ret = ab8500_chargalg_kick_watchdog(di); if (ret < 0) dev_err(di->dev, "failed to kick watchdog\n"); @@ -1685,21 +1685,21 @@ static void abx500_chargalg_wd_work(struct work_struct *work) } /** - * abx500_chargalg_work() - Work to run the charging algorithm instantly + * ab8500_chargalg_work() - Work to run the charging algorithm instantly * @work: pointer to the work_struct structure * * Work queue function for calling the charging algorithm */ -static void abx500_chargalg_work(struct work_struct *work) +static void ab8500_chargalg_work(struct work_struct *work) { - struct abx500_chargalg *di = container_of(work, - struct abx500_chargalg, chargalg_work); + struct ab8500_chargalg *di = container_of(work, + struct ab8500_chargalg, chargalg_work); - abx500_chargalg_algorithm(di); + ab8500_chargalg_algorithm(di); } /** - * abx500_chargalg_get_property() - get the chargalg properties + * ab8500_chargalg_get_property() - get the chargalg properties * @psy: pointer to the power_supply structure * @psp: pointer to the power_supply_property structure * @val: pointer to the power_supply_propval union @@ -1710,11 +1710,11 @@ static void abx500_chargalg_work(struct work_struct *work) * health: health of the battery * Returns error code in case of failure else 0 on success */ -static int abx500_chargalg_get_property(struct power_supply *psy, +static int ab8500_chargalg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct abx500_chargalg *di = power_supply_get_drvdata(psy); + struct ab8500_chargalg *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -1743,13 +1743,13 @@ static int abx500_chargalg_get_property(struct power_supply *psy, /* Exposure to the sysfs interface */ -static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di, +static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di, char *buf) { return sprintf(buf, "%d\n", di->curr_status.curr_step); } -static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di, +static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di, const char *buf, size_t length) { long int param; @@ -1774,7 +1774,7 @@ static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di, } -static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di, +static ssize_t ab8500_chargalg_en_show(struct ab8500_chargalg *di, char *buf) { return sprintf(buf, "%d\n", @@ -1782,7 +1782,7 @@ static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di, di->susp_status.usb_suspended); } -static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, +static ssize_t ab8500_chargalg_en_store(struct ab8500_chargalg *di, const char *buf, size_t length) { long int param; @@ -1829,22 +1829,22 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, return strlen(buf); } -static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger = - __ATTR(chargalg, 0644, abx500_chargalg_en_show, - abx500_chargalg_en_store); +static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_en_charger = + __ATTR(chargalg, 0644, ab8500_chargalg_en_show, + ab8500_chargalg_en_store); -static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step = - __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show, - abx500_chargalg_curr_step_store); +static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_curr_step = + __ATTR(chargalg_curr_step, 0644, ab8500_chargalg_curr_step_show, + ab8500_chargalg_curr_step_store); -static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj, +static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct abx500_chargalg_sysfs_entry *entry = container_of(attr, - struct abx500_chargalg_sysfs_entry, attr); + struct ab8500_chargalg_sysfs_entry *entry = container_of(attr, + struct ab8500_chargalg_sysfs_entry, attr); - struct abx500_chargalg *di = container_of(kobj, - struct abx500_chargalg, chargalg_kobject); + struct ab8500_chargalg *di = container_of(kobj, + struct ab8500_chargalg, chargalg_kobject); if (!entry->show) return -EIO; @@ -1852,14 +1852,14 @@ static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj, return entry->show(di, buf); } -static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, +static ssize_t ab8500_chargalg_sysfs_charger(struct kobject *kobj, struct attribute *attr, const char *buf, size_t length) { - struct abx500_chargalg_sysfs_entry *entry = container_of(attr, - struct abx500_chargalg_sysfs_entry, attr); + struct ab8500_chargalg_sysfs_entry *entry = container_of(attr, + struct ab8500_chargalg_sysfs_entry, attr); - struct abx500_chargalg *di = container_of(kobj, - struct abx500_chargalg, chargalg_kobject); + struct ab8500_chargalg *di = container_of(kobj, + struct ab8500_chargalg, chargalg_kobject); if (!entry->store) return -EIO; @@ -1867,47 +1867,47 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, return entry->store(di, buf, length); } -static struct attribute *abx500_chargalg_chg[] = { - &abx500_chargalg_en_charger.attr, - &abx500_chargalg_curr_step.attr, +static struct attribute *ab8500_chargalg_chg[] = { + &ab8500_chargalg_en_charger.attr, + &ab8500_chargalg_curr_step.attr, NULL, }; -static const struct sysfs_ops abx500_chargalg_sysfs_ops = { - .show = abx500_chargalg_sysfs_show, - .store = abx500_chargalg_sysfs_charger, +static const struct sysfs_ops ab8500_chargalg_sysfs_ops = { + .show = ab8500_chargalg_sysfs_show, + .store = ab8500_chargalg_sysfs_charger, }; -static struct kobj_type abx500_chargalg_ktype = { - .sysfs_ops = &abx500_chargalg_sysfs_ops, - .default_attrs = abx500_chargalg_chg, +static struct kobj_type ab8500_chargalg_ktype = { + .sysfs_ops = &ab8500_chargalg_sysfs_ops, + .default_attrs = ab8500_chargalg_chg, }; /** - * abx500_chargalg_sysfs_exit() - de-init of sysfs entry - * @di: pointer to the struct abx500_chargalg + * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry + * @di: pointer to the struct ab8500_chargalg * * This function removes the entry in sysfs. */ -static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di) +static void ab8500_chargalg_sysfs_exit(struct ab8500_chargalg *di) { kobject_del(&di->chargalg_kobject); } /** - * abx500_chargalg_sysfs_init() - init of sysfs entry - * @di: pointer to the struct abx500_chargalg + * ab8500_chargalg_sysfs_init() - init of sysfs entry + * @di: pointer to the struct ab8500_chargalg * * This function adds an entry in sysfs. * Returns error code in case of failure else 0(on success) */ -static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di) +static int ab8500_chargalg_sysfs_init(struct ab8500_chargalg *di) { int ret = 0; ret = kobject_init_and_add(&di->chargalg_kobject, - &abx500_chargalg_ktype, - NULL, "abx500_chargalg"); + &ab8500_chargalg_ktype, + NULL, "ab8500_chargalg"); if (ret < 0) dev_err(di->dev, "failed to create sysfs entry\n"); @@ -1915,9 +1915,9 @@ static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di) } /* Exposure to the sysfs interface <> */ -static int __maybe_unused abx500_chargalg_resume(struct device *dev) +static int __maybe_unused ab8500_chargalg_resume(struct device *dev) { - struct abx500_chargalg *di = dev_get_drvdata(dev); + struct ab8500_chargalg *di = dev_get_drvdata(dev); /* Kick charger watchdog if charging (any charger online) */ if (di->chg_info.online_chg) @@ -1932,9 +1932,9 @@ static int __maybe_unused abx500_chargalg_resume(struct device *dev) return 0; } -static int __maybe_unused abx500_chargalg_suspend(struct device *dev) +static int __maybe_unused ab8500_chargalg_suspend(struct device *dev) { - struct abx500_chargalg *di = dev_get_drvdata(dev); + struct ab8500_chargalg *di = dev_get_drvdata(dev); if (di->chg_info.online_chg) cancel_delayed_work_sync(&di->chargalg_wd_work); @@ -1948,22 +1948,22 @@ static char *supply_interface[] = { "ab8500_fg", }; -static const struct power_supply_desc abx500_chargalg_desc = { +static const struct power_supply_desc ab8500_chargalg_desc = { .name = "abx500_chargalg", .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = abx500_chargalg_props, - .num_properties = ARRAY_SIZE(abx500_chargalg_props), - .get_property = abx500_chargalg_get_property, - .external_power_changed = abx500_chargalg_external_power_changed, + .properties = ab8500_chargalg_props, + .num_properties = ARRAY_SIZE(ab8500_chargalg_props), + .get_property = ab8500_chargalg_get_property, + .external_power_changed = ab8500_chargalg_external_power_changed, }; -static int abx500_chargalg_bind(struct device *dev, struct device *master, +static int ab8500_chargalg_bind(struct device *dev, struct device *master, void *data) { - struct abx500_chargalg *di = dev_get_drvdata(dev); + struct ab8500_chargalg *di = dev_get_drvdata(dev); /* Create a work queue for the chargalg */ - di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq", + di->chargalg_wq = alloc_ordered_workqueue("ab8500_chargalg_wq", WQ_MEM_RECLAIM); if (di->chargalg_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); @@ -1976,10 +1976,10 @@ static int abx500_chargalg_bind(struct device *dev, struct device *master, return 0; } -static void abx500_chargalg_unbind(struct device *dev, struct device *master, +static void ab8500_chargalg_unbind(struct device *dev, struct device *master, void *data) { - struct abx500_chargalg *di = dev_get_drvdata(dev); + struct ab8500_chargalg *di = dev_get_drvdata(dev); /* Stop all timers and work */ hrtimer_cancel(&di->safety_timer); @@ -1994,16 +1994,16 @@ static void abx500_chargalg_unbind(struct device *dev, struct device *master, flush_scheduled_work(); } -static const struct component_ops abx500_chargalg_component_ops = { - .bind = abx500_chargalg_bind, - .unbind = abx500_chargalg_unbind, +static const struct component_ops ab8500_chargalg_component_ops = { + .bind = ab8500_chargalg_bind, + .unbind = ab8500_chargalg_unbind, }; -static int abx500_chargalg_probe(struct platform_device *pdev) +static int ab8500_chargalg_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct power_supply_config psy_cfg = {}; - struct abx500_chargalg *di; + struct ab8500_chargalg *di; int ret = 0; di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); @@ -2022,28 +2022,28 @@ static int abx500_chargalg_probe(struct platform_device *pdev) /* Initilialize safety timer */ hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); - di->safety_timer.function = abx500_chargalg_safety_timer_expired; + di->safety_timer.function = ab8500_chargalg_safety_timer_expired; /* Initilialize maintenance timer */ hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); di->maintenance_timer.function = - abx500_chargalg_maintenance_timer_expired; + ab8500_chargalg_maintenance_timer_expired; /* Init work for chargalg */ INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, - abx500_chargalg_periodic_work); + ab8500_chargalg_periodic_work); INIT_DEFERRABLE_WORK(&di->chargalg_wd_work, - abx500_chargalg_wd_work); + ab8500_chargalg_wd_work); /* Init work for chargalg */ - INIT_WORK(&di->chargalg_work, abx500_chargalg_work); + INIT_WORK(&di->chargalg_work, ab8500_chargalg_work); /* To detect charger at startup */ di->chg_info.prev_conn_chg = -1; /* Register chargalg power supply class */ di->chargalg_psy = devm_power_supply_register(di->dev, - &abx500_chargalg_desc, + &ab8500_chargalg_desc, &psy_cfg); if (IS_ERR(di->chargalg_psy)) { dev_err(di->dev, "failed to register chargalg psy\n"); @@ -2053,7 +2053,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); /* sysfs interface to enable/disable charging from user space */ - ret = abx500_chargalg_sysfs_init(di); + ret = ab8500_chargalg_sysfs_init(di); if (ret) { dev_err(di->dev, "failed to create sysfs entry\n"); return ret; @@ -2061,38 +2061,38 @@ static int abx500_chargalg_probe(struct platform_device *pdev) di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH; dev_info(di->dev, "probe success\n"); - return component_add(dev, &abx500_chargalg_component_ops); + return component_add(dev, &ab8500_chargalg_component_ops); } -static int abx500_chargalg_remove(struct platform_device *pdev) +static int ab8500_chargalg_remove(struct platform_device *pdev) { - struct abx500_chargalg *di = platform_get_drvdata(pdev); + struct ab8500_chargalg *di = platform_get_drvdata(pdev); - component_del(&pdev->dev, &abx500_chargalg_component_ops); + component_del(&pdev->dev, &ab8500_chargalg_component_ops); /* sysfs interface to enable/disable charging from user space */ - abx500_chargalg_sysfs_exit(di); + ab8500_chargalg_sysfs_exit(di); return 0; } -static SIMPLE_DEV_PM_OPS(abx500_chargalg_pm_ops, abx500_chargalg_suspend, abx500_chargalg_resume); +static SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume); static const struct of_device_id ab8500_chargalg_match[] = { { .compatible = "stericsson,ab8500-chargalg", }, { }, }; -struct platform_driver abx500_chargalg_driver = { - .probe = abx500_chargalg_probe, - .remove = abx500_chargalg_remove, +struct platform_driver ab8500_chargalg_driver = { + .probe = ab8500_chargalg_probe, + .remove = ab8500_chargalg_remove, .driver = { - .name = "ab8500-chargalg", + .name = "ab8500_chargalg", .of_match_table = ab8500_chargalg_match, - .pm = &abx500_chargalg_pm_ops, + .pm = &ab8500_chargalg_pm_ops, }, }; MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); -MODULE_ALIAS("platform:abx500-chargalg"); -MODULE_DESCRIPTION("abx500 battery charging algorithm"); +MODULE_ALIAS("platform:ab8500-chargalg"); +MODULE_DESCRIPTION("ab8500 battery charging algorithm"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index fa49e12e5a60..e0d3d6bd8b8c 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3388,7 +3388,7 @@ static const struct component_master_ops ab8500_charger_comp_ops = { static struct platform_driver *const ab8500_charger_component_drivers[] = { &ab8500_fg_driver, &ab8500_btemp_driver, - &abx500_chargalg_driver, + &ab8500_chargalg_driver, }; static int ab8500_charger_compare_dev(struct device *dev, void *data) From 484a9cc3dcb867813fca62f6443c1e77a1ae3c27 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Jul 2021 17:27:07 +0200 Subject: [PATCH 050/177] power: supply: ab8500: Drop abx500 concept Drop the entire idea with abx500 being abstract and different from ab8500 in the AB8500 charging drivers. This rids the two identical definitions of a slew of structs in ab8500-bm.h and makes things less confusion and easier to understand. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500-bm.h | 285 ++++++------------------- drivers/power/supply/ab8500_bmdata.c | 32 +-- drivers/power/supply/ab8500_btemp.c | 16 +- drivers/power/supply/ab8500_chargalg.c | 2 +- drivers/power/supply/ab8500_charger.c | 2 +- drivers/power/supply/ab8500_fg.c | 6 +- 6 files changed, 95 insertions(+), 248 deletions(-) diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h index 4e417fbae60c..d11405b7ee1a 100644 --- a/drivers/power/supply/ab8500-bm.h +++ b/drivers/power/supply/ab8500-bm.h @@ -269,43 +269,43 @@ enum bup_vch_sel { /* * ADC for the battery thermistor. - * When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined + * When using the AB8500_ADC_THERM_BATCTRL the battery ID resistor is combined * with a NTC resistor to both identify the battery and to measure its * temperature. Different phone manufactures uses different techniques to both * identify the battery and to read its temperature. */ -enum abx500_adc_therm { - ABx500_ADC_THERM_BATCTRL, - ABx500_ADC_THERM_BATTEMP, +enum ab8500_adc_therm { + AB8500_ADC_THERM_BATCTRL, + AB8500_ADC_THERM_BATTEMP, }; /** - * struct abx500_res_to_temp - defines one point in a temp to res curve. To + * struct ab8500_res_to_temp - defines one point in a temp to res curve. To * be used in battery packs that combines the identification resistor with a * NTC resistor. * @temp: battery pack temperature in Celsius * @resist: NTC resistor net total resistance */ -struct abx500_res_to_temp { +struct ab8500_res_to_temp { int temp; int resist; }; /** - * struct abx500_v_to_cap - Table for translating voltage to capacity + * struct ab8500_v_to_cap - Table for translating voltage to capacity * @voltage: Voltage in mV * @capacity: Capacity in percent */ -struct abx500_v_to_cap { +struct ab8500_v_to_cap { int voltage; int capacity; }; /* Forward declaration */ -struct abx500_fg; +struct ab8500_fg; /** - * struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds + * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds * if not specified * @recovery_sleep_timer: Time between measurements while recovering * @recovery_total_time: Total recovery time @@ -333,7 +333,7 @@ struct abx500_fg; * @pcut_max_restart: Max number of restarts * @pcut_debounce_time: Sets battery debounce time */ -struct abx500_fg_parameters { +struct ab8500_fg_parameters { int recovery_sleep_timer; int recovery_total_time; int init_timer; @@ -357,13 +357,13 @@ struct abx500_fg_parameters { }; /** - * struct abx500_charger_maximization - struct used by the board config. + * struct ab8500_charger_maximization - struct used by the board config. * @use_maxi: Enable maximization for this battery type * @maxi_chg_curr: Maximum charger current allowed * @maxi_wait_cycles: cycles to wait before setting charger current * @charger_curr_step delta between two charger current settings (mA) */ -struct abx500_maxim_parameters { +struct ab8500_maxim_parameters { bool ena_maxi; int chg_curr; int wait_cycles; @@ -371,7 +371,7 @@ struct abx500_maxim_parameters { }; /** - * struct abx500_battery_type - different batteries supported + * struct ab8500_battery_type - different batteries supported * @name: battery technology * @resis_high: battery upper resistance limit * @resis_low: battery lower resistance limit @@ -400,7 +400,7 @@ struct abx500_maxim_parameters { * @n_batres_tbl_elements number of elements in the batres_tbl * @batres_tbl battery internal resistance vs temperature table */ -struct abx500_battery_type { +struct ab8500_battery_type { int name; int resis_high; int resis_low; @@ -421,210 +421,13 @@ struct abx500_battery_type { int low_high_vol_lvl; int battery_resistance; int n_temp_tbl_elements; - const struct abx500_res_to_temp *r_to_t_tbl; + const struct ab8500_res_to_temp *r_to_t_tbl; int n_v_cap_tbl_elements; - const struct abx500_v_to_cap *v_to_cap_tbl; + const struct ab8500_v_to_cap *v_to_cap_tbl; int n_batres_tbl_elements; const struct batres_vs_temp *batres_tbl; }; -/** - * struct abx500_bm_capacity_levels - abx500 capacity level data - * @critical: critical capacity level in percent - * @low: low capacity level in percent - * @normal: normal capacity level in percent - * @high: high capacity level in percent - * @full: full capacity level in percent - */ -struct abx500_bm_capacity_levels { - int critical; - int low; - int normal; - int high; - int full; -}; - -/** - * struct abx500_bm_charger_parameters - Charger specific parameters - * @usb_volt_max: maximum allowed USB charger voltage in mV - * @usb_curr_max: maximum allowed USB charger current in mA - * @ac_volt_max: maximum allowed AC charger voltage in mV - * @ac_curr_max: maximum allowed AC charger current in mA - */ -struct abx500_bm_charger_parameters { - int usb_volt_max; - int usb_curr_max; - int ac_volt_max; - int ac_curr_max; -}; - -/** - * struct abx500_bm_data - abx500 battery management data - * @temp_under under this temp, charging is stopped - * @temp_low between this temp and temp_under charging is reduced - * @temp_high between this temp and temp_over charging is reduced - * @temp_over over this temp, charging is stopped - * @temp_now present battery temperature - * @temp_interval_chg temperature measurement interval in s when charging - * @temp_interval_nochg temperature measurement interval in s when not charging - * @main_safety_tmr_h safety timer for main charger - * @usb_safety_tmr_h safety timer for usb charger - * @bkup_bat_v voltage which we charge the backup battery with - * @bkup_bat_i current which we charge the backup battery with - * @no_maintenance indicates that maintenance charging is disabled - * @capacity_scaling indicates whether capacity scaling is to be used - * @abx500_adc_therm placement of thermistor, batctrl or battemp adc - * @chg_unknown_bat flag to enable charging of unknown batteries - * @enable_overshoot flag to enable VBAT overshoot control - * @auto_trig flag to enable auto adc trigger - * @fg_res resistance of FG resistor in 0.1mOhm - * @n_btypes number of elements in array bat_type - * @batt_id index of the identified battery in array bat_type - * @interval_charging charge alg cycle period time when charging (sec) - * @interval_not_charging charge alg cycle period time when not charging (sec) - * @temp_hysteresis temperature hysteresis - * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm) - * @n_chg_out_curr number of elements in array chg_output_curr - * @n_chg_in_curr number of elements in array chg_input_curr - * @chg_output_curr charger output current level map - * @chg_input_curr charger input current level map - * @maxi maximization parameters - * @cap_levels capacity in percent for the different capacity levels - * @bat_type table of supported battery types - * @chg_params charger parameters - * @fg_params fuel gauge parameters - */ -struct abx500_bm_data { - int temp_under; - int temp_low; - int temp_high; - int temp_over; - int temp_now; - int temp_interval_chg; - int temp_interval_nochg; - int main_safety_tmr_h; - int usb_safety_tmr_h; - int bkup_bat_v; - int bkup_bat_i; - bool no_maintenance; - bool capacity_scaling; - bool chg_unknown_bat; - bool enable_overshoot; - bool auto_trig; - enum abx500_adc_therm adc_therm; - int fg_res; - int n_btypes; - int batt_id; - int interval_charging; - int interval_not_charging; - int temp_hysteresis; - int gnd_lift_resistance; - int n_chg_out_curr; - int n_chg_in_curr; - int *chg_output_curr; - int *chg_input_curr; - const struct abx500_maxim_parameters *maxi; - const struct abx500_bm_capacity_levels *cap_levels; - struct abx500_battery_type *bat_type; - const struct abx500_bm_charger_parameters *chg_params; - const struct abx500_fg_parameters *fg_params; -}; - -enum { - NTC_EXTERNAL = 0, - NTC_INTERNAL, -}; - -/** - * struct res_to_temp - defines one point in a temp to res curve. To - * be used in battery packs that combines the identification resistor with a - * NTC resistor. - * @temp: battery pack temperature in Celsius - * @resist: NTC resistor net total resistance - */ -struct res_to_temp { - int temp; - int resist; -}; - -/** - * struct batres_vs_temp - defines one point in a temp vs battery internal - * resistance curve. - * @temp: battery pack temperature in Celsius - * @resist: battery internal reistance in mOhm - */ -struct batres_vs_temp { - int temp; - int resist; -}; - -/* Forward declaration */ -struct ab8500_fg; - -/** - * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds - * if not specified - * @recovery_sleep_timer: Time between measurements while recovering - * @recovery_total_time: Total recovery time - * @init_timer: Measurement interval during startup - * @init_discard_time: Time we discard voltage measurement at startup - * @init_total_time: Total init time during startup - * @high_curr_time: Time current has to be high to go to recovery - * @accu_charging: FG accumulation time while charging - * @accu_high_curr: FG accumulation time in high current mode - * @high_curr_threshold: High current threshold, in mA - * @lowbat_threshold: Low battery threshold, in mV - * @battok_falling_th_sel0 Threshold in mV for battOk signal sel0 - * Resolution in 50 mV step. - * @battok_raising_th_sel1 Threshold in mV for battOk signal sel1 - * Resolution in 50 mV step. - * @user_cap_limit Capacity reported from user must be within this - * limit to be considered as sane, in percentage - * points. - * @maint_thres This is the threshold where we stop reporting - * battery full while in maintenance, in per cent - * @pcut_enable: Enable power cut feature in ab8505 - * @pcut_max_time: Max time threshold - * @pcut_flag_time: Flagtime threshold - * @pcut_max_restart: Max number of restarts - * @pcut_debunce_time: Sets battery debounce time - */ -struct ab8500_fg_parameters { - int recovery_sleep_timer; - int recovery_total_time; - int init_timer; - int init_discard_time; - int init_total_time; - int high_curr_time; - int accu_charging; - int accu_high_curr; - int high_curr_threshold; - int lowbat_threshold; - int battok_falling_th_sel0; - int battok_raising_th_sel1; - int user_cap_limit; - int maint_thres; - bool pcut_enable; - u8 pcut_max_time; - u8 pcut_flag_time; - u8 pcut_max_restart; - u8 pcut_debunce_time; -}; - -/** - * struct ab8500_charger_maximization - struct used by the board config. - * @use_maxi: Enable maximization for this battery type - * @maxi_chg_curr: Maximum charger current allowed - * @maxi_wait_cycles: cycles to wait before setting charger current - * @charger_curr_step delta between two charger current settings (mA) - */ -struct ab8500_maxim_parameters { - bool ena_maxi; - int chg_curr; - int wait_cycles; - int charger_curr_step; -}; - /** * struct ab8500_bm_capacity_levels - ab8500 capacity level data * @critical: critical capacity level in percent @@ -661,6 +464,7 @@ struct ab8500_bm_charger_parameters { * @temp_low between this temp and temp_under charging is reduced * @temp_high between this temp and temp_over charging is reduced * @temp_over over this temp, charging is stopped + * @temp_now present battery temperature * @temp_interval_chg temperature measurement interval in s when charging * @temp_interval_nochg temperature measurement interval in s when not charging * @main_safety_tmr_h safety timer for main charger @@ -669,9 +473,10 @@ struct ab8500_bm_charger_parameters { * @bkup_bat_i current which we charge the backup battery with * @no_maintenance indicates that maintenance charging is disabled * @capacity_scaling indicates whether capacity scaling is to be used - * @adc_therm placement of thermistor, batctrl or battemp adc + * @ab8500_adc_therm placement of thermistor, batctrl or battemp adc * @chg_unknown_bat flag to enable charging of unknown batteries * @enable_overshoot flag to enable VBAT overshoot control + * @auto_trig flag to enable auto adc trigger * @fg_res resistance of FG resistor in 0.1mOhm * @n_btypes number of elements in array bat_type * @batt_id index of the identified battery in array bat_type @@ -679,7 +484,11 @@ struct ab8500_bm_charger_parameters { * @interval_not_charging charge alg cycle period time when not charging (sec) * @temp_hysteresis temperature hysteresis * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm) - * @maxi: maximization parameters + * @n_chg_out_curr number of elements in array chg_output_curr + * @n_chg_in_curr number of elements in array chg_input_curr + * @chg_output_curr charger output current level map + * @chg_input_curr charger input current level map + * @maxi maximization parameters * @cap_levels capacity in percent for the different capacity levels * @bat_type table of supported battery types * @chg_params charger parameters @@ -690,6 +499,7 @@ struct ab8500_bm_data { int temp_low; int temp_high; int temp_over; + int temp_now; int temp_interval_chg; int temp_interval_nochg; int main_safety_tmr_h; @@ -700,7 +510,8 @@ struct ab8500_bm_data { bool capacity_scaling; bool chg_unknown_bat; bool enable_overshoot; - enum abx500_adc_therm adc_therm; + bool auto_trig; + enum ab8500_adc_therm adc_therm; int fg_res; int n_btypes; int batt_id; @@ -708,13 +519,49 @@ struct ab8500_bm_data { int interval_not_charging; int temp_hysteresis; int gnd_lift_resistance; + int n_chg_out_curr; + int n_chg_in_curr; + int *chg_output_curr; + int *chg_input_curr; const struct ab8500_maxim_parameters *maxi; const struct ab8500_bm_capacity_levels *cap_levels; + struct ab8500_battery_type *bat_type; const struct ab8500_bm_charger_parameters *chg_params; const struct ab8500_fg_parameters *fg_params; }; -extern struct abx500_bm_data ab8500_bm_data; +enum { + NTC_EXTERNAL = 0, + NTC_INTERNAL, +}; + +/** + * struct res_to_temp - defines one point in a temp to res curve. To + * be used in battery packs that combines the identification resistor with a + * NTC resistor. + * @temp: battery pack temperature in Celsius + * @resist: NTC resistor net total resistance + */ +struct res_to_temp { + int temp; + int resist; +}; + +/** + * struct batres_vs_temp - defines one point in a temp vs battery internal + * resistance curve. + * @temp: battery pack temperature in Celsius + * @resist: battery internal reistance in mOhm + */ +struct batres_vs_temp { + int temp; + int resist; +}; + +/* Forward declaration */ +struct ab8500_fg; + +extern struct ab8500_bm_data ab8500_bm_data; void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA); struct ab8500_fg *ab8500_fg_get(void); @@ -725,7 +572,7 @@ int ab8500_fg_inst_curr_started(struct ab8500_fg *di); int ab8500_fg_inst_curr_done(struct ab8500_fg *di); int ab8500_bm_of_probe(struct device *dev, struct device_node *np, - struct abx500_bm_data *bm); + struct ab8500_bm_data *bm); extern struct platform_driver ab8500_fg_driver; extern struct platform_driver ab8500_btemp_driver; diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index c2b8c0bb77e2..f705c19ef359 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -13,7 +13,7 @@ * Note that the res_to_temp table must be strictly sorted by falling resistance * values to work. */ -const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = { +const struct ab8500_res_to_temp ab8500_temp_tbl_a_thermistor[] = { {-5, 53407}, { 0, 48594}, { 5, 43804}, @@ -35,7 +35,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor); const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor); EXPORT_SYMBOL(ab8500_temp_tbl_a_size); -const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = { +const struct ab8500_res_to_temp ab8500_temp_tbl_b_thermistor[] = { {-5, 200000}, { 0, 159024}, { 5, 151921}, @@ -57,7 +57,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor); const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor); EXPORT_SYMBOL(ab8500_temp_tbl_b_size); -static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = { +static const struct ab8500_v_to_cap cap_tbl_a_thermistor[] = { {4171, 100}, {4114, 95}, {4009, 83}, @@ -80,7 +80,7 @@ static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = { {3247, 0}, }; -static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = { +static const struct ab8500_v_to_cap cap_tbl_b_thermistor[] = { {4161, 100}, {4124, 98}, {4044, 90}, @@ -103,7 +103,7 @@ static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = { {3250, 0}, }; -static const struct abx500_v_to_cap cap_tbl[] = { +static const struct ab8500_v_to_cap cap_tbl[] = { {4186, 100}, {4163, 99}, {4114, 95}, @@ -134,7 +134,7 @@ static const struct abx500_v_to_cap cap_tbl[] = { * Note that the res_to_temp table must be strictly sorted by falling * resistance values to work. */ -static const struct abx500_res_to_temp temp_tbl[] = { +static const struct ab8500_res_to_temp temp_tbl[] = { {-5, 214834}, { 0, 162943}, { 5, 124820}, @@ -191,7 +191,7 @@ static const struct batres_vs_temp temp_to_batres_tbl_9100[] = { {-20, 180}, }; -static struct abx500_battery_type bat_type_thermistor[] = { +static struct ab8500_battery_type bat_type_thermistor[] = { [BATTERY_UNKNOWN] = { /* First element always represent the UNKNOWN battery */ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, @@ -277,7 +277,7 @@ static struct abx500_battery_type bat_type_thermistor[] = { }, }; -static struct abx500_battery_type bat_type_ext_thermistor[] = { +static struct ab8500_battery_type bat_type_ext_thermistor[] = { [BATTERY_UNKNOWN] = { /* First element always represent the UNKNOWN battery */ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, @@ -394,7 +394,7 @@ static struct abx500_battery_type bat_type_ext_thermistor[] = { }, }; -static const struct abx500_bm_capacity_levels cap_levels = { +static const struct ab8500_bm_capacity_levels cap_levels = { .critical = 2, .low = 10, .normal = 70, @@ -402,7 +402,7 @@ static const struct abx500_bm_capacity_levels cap_levels = { .full = 100, }; -static const struct abx500_fg_parameters fg = { +static const struct ab8500_fg_parameters fg = { .recovery_sleep_timer = 10, .recovery_total_time = 100, .init_timer = 1, @@ -424,14 +424,14 @@ static const struct abx500_fg_parameters fg = { .pcut_debounce_time = 2, }; -static const struct abx500_maxim_parameters ab8500_maxi_params = { +static const struct ab8500_maxim_parameters ab8500_maxi_params = { .ena_maxi = true, .chg_curr = 910, .wait_cycles = 10, .charger_curr_step = 100, }; -static const struct abx500_bm_charger_parameters chg = { +static const struct ab8500_bm_charger_parameters chg = { .usb_volt_max = 5500, .usb_curr_max = 1500, .ac_volt_max = 7500, @@ -456,7 +456,7 @@ static int ab8500_charge_input_curr_map[] = { 700, 800, 900, 1000, 1100, 1300, 1400, 1500, }; -struct abx500_bm_data ab8500_bm_data = { +struct ab8500_bm_data ab8500_bm_data = { .temp_under = 3, .temp_low = 8, .temp_high = 43, @@ -469,7 +469,7 @@ struct abx500_bm_data ab8500_bm_data = { .bkup_bat_i = BUP_ICH_SEL_150UA, .no_maintenance = false, .capacity_scaling = false, - .adc_therm = ABx500_ADC_THERM_BATCTRL, + .adc_therm = AB8500_ADC_THERM_BATCTRL, .chg_unknown_bat = false, .enable_overshoot = false, .fg_res = 100, @@ -492,7 +492,7 @@ struct abx500_bm_data ab8500_bm_data = { int ab8500_bm_of_probe(struct device *dev, struct device_node *np, - struct abx500_bm_data *bm) + struct ab8500_bm_data *bm) { const struct batres_vs_temp *tmp_batres_tbl; struct device_node *battery_node; @@ -531,7 +531,7 @@ int ab8500_bm_of_probe(struct device *dev, } else { bm->n_btypes = 4; bm->bat_type = bat_type_ext_thermistor; - bm->adc_therm = ABx500_ADC_THERM_BATTEMP; + bm->adc_therm = AB8500_ADC_THERM_BATTEMP; tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor; } diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 24958b935d39..b6c9111d77d7 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -103,7 +103,7 @@ struct ab8500_btemp { struct iio_channel *btemp_ball; struct iio_channel *bat_ctrl; struct ab8500_fg *fg; - struct abx500_bm_data *bm; + struct ab8500_bm_data *bm; struct power_supply *btemp_psy; struct ab8500_btemp_events events; struct ab8500_btemp_ranges btemp_ranges; @@ -145,7 +145,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, return (450000 * (v_batctrl)) / (1800 - v_batctrl); } - if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) { + if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) { /* * If the battery has internal NTC, we use the current * source to calculate the resistance. @@ -207,7 +207,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, return 0; /* Only do this for batteries with internal NTC */ - if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) { + if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && enable) { if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA) curr = BAT_CTRL_7U_ENA; @@ -240,7 +240,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, __func__); goto disable_curr_source; } - } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) { + } else if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && !enable) { dev_dbg(di->dev, "Disable BATCTRL curr source\n"); /* Write 0 to the curr bits */ @@ -418,7 +418,7 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di) * based on the NTC resistance. */ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, - const struct abx500_res_to_temp *tbl, int tbl_size, int res) + const struct ab8500_res_to_temp *tbl, int tbl_size, int res) { int i; /* @@ -458,7 +458,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) id = di->bm->batt_id; - if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && + if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && id != BATTERY_UNKNOWN) { rbat = ab8500_btemp_get_batctrl_res(di); @@ -527,7 +527,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) dev_dbg(di->dev, "Battery detected on %s" " low %d < res %d < high: %d" " index: %d\n", - di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ? + di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ? "BATCTRL" : "BATTEMP", di->bm->bat_type[i].resis_low, res, di->bm->bat_type[i].resis_high, i); @@ -547,7 +547,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) * We only have to change current source if the * detected type is Type 1. */ - if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && + if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && di->bm->batt_id == 1) { dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA; diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index b0bbb1c4b83a..8dd66eb18fd5 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -249,7 +249,7 @@ struct ab8500_chargalg { struct ab8500_chargalg_suspension_status susp_status; struct ab8500 *parent; struct ab8500_chargalg_current_step_status curr_status; - struct abx500_bm_data *bm; + struct ab8500_bm_data *bm; struct power_supply *chargalg_psy; struct ux500_charger *ac_chg; struct ux500_charger *usb_chg; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index e0d3d6bd8b8c..15eadaf46f14 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -292,7 +292,7 @@ struct ab8500_charger { struct iio_channel *adc_main_charger_c; struct iio_channel *adc_vbus_v; struct iio_channel *adc_usb_charger_c; - struct abx500_bm_data *bm; + struct ab8500_bm_data *bm; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; struct ab8500_charger_max_usb_in_curr max_usb_in_curr; diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index bdbf3f13bee0..21bb2fd6725b 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -225,7 +225,7 @@ struct ab8500_fg { struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; struct iio_channel *main_bat_v; - struct abx500_bm_data *bm; + struct ab8500_bm_data *bm; struct power_supply *fg_psy; struct workqueue_struct *fg_wq; struct delayed_work fg_periodic_work; @@ -854,7 +854,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di) static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage) { int i, tbl_size; - const struct abx500_v_to_cap *tbl; + const struct ab8500_v_to_cap *tbl; int cap = 0; tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl; @@ -2233,7 +2233,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) case POWER_SUPPLY_TYPE_BATTERY: if (!di->flags.batt_id_received && di->bm->batt_id != BATTERY_UNKNOWN) { - const struct abx500_battery_type *b; + const struct ab8500_battery_type *b; b = &(di->bm->bat_type[di->bm->batt_id]); From 661d10ee0f1be7e3e08267b8364439980d02a42c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Jul 2021 17:27:08 +0200 Subject: [PATCH 051/177] power: supply: ab8500: Rename charging algorithm psy If we rename the "abx500_chargalg" supply to "ab8500_chargalg" as it should be named, the existing supplies are supplying that supply but that was obviously not working since it had the wrong name. Now that the dependency kicks in we get a bunch of NULL references from ab8500_chargalg_external_power_changed() so check that the workqueue is allocated before we try to queue work on it. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_chargalg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 8dd66eb18fd5..4ef34f64e9c0 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -1275,7 +1275,8 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy) * Trigger execution of the algorithm instantly and read * all power_supply properties there instead */ - queue_work(di->chargalg_wq, &di->chargalg_work); + if (di->chargalg_wq) + queue_work(di->chargalg_wq, &di->chargalg_work); } /** @@ -1949,7 +1950,7 @@ static char *supply_interface[] = { }; static const struct power_supply_desc ab8500_chargalg_desc = { - .name = "abx500_chargalg", + .name = "ab8500_chargalg", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = ab8500_chargalg_props, .num_properties = ARRAY_SIZE(ab8500_chargalg_props), From 5176a18bb5e1596d46c34c4700ac67b74f88f704 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Jul 2021 17:27:09 +0200 Subject: [PATCH 052/177] power: supply: ab8500: Drop some includes from bmdata This file isn't using any AB8500 symbols so drop these includes. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_bmdata.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index f705c19ef359..6f5fb794042c 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -2,8 +2,6 @@ #include #include #include -#include -#include #include "ab8500-bm.h" From 56d629af09b9d4db9792257165844287ecce0a98 Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Wed, 16 Jun 2021 11:51:24 -0700 Subject: [PATCH 053/177] power: supply: PCHG: Peripheral device charger This patch adds a driver for PCHG (Peripheral CHarGer). PCHG is a framework managing power supplies for peripheral devices. This driver creates a sysfs node for each peripheral charge port: /sys/class/power_supply/peripheral where is the index of a charge port. For example, when a stylus is connected to a NFC/WLC port, the node returns: /sys/class/power_supply/peripheral0/ capacity=50 charge_type=Standard scope=Device status=Charging type=Battery Signed-off-by: Daisuke Nojiri Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 10 + drivers/power/supply/Makefile | 1 + .../power/supply/cros_peripheral_charger.c | 386 ++++++++++++++++++ .../linux/platform_data/cros_ec_commands.h | 67 +++ 4 files changed, 464 insertions(+) create mode 100644 drivers/power/supply/cros_peripheral_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 11f5368e810e..47b7d2111c4e 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -736,6 +736,16 @@ config CHARGER_CROS_USBPD what is connected to USB PD ports from the EC and converts that into power_supply properties. +config CHARGER_CROS_PCHG + tristate "ChromeOS EC based peripheral charger" + depends on MFD_CROS_EC_DEV + default MFD_CROS_EC_DEV + help + Say Y here to enable ChromeOS EC based peripheral charge driver. + This driver gets various information about the devices connected to + the peripheral charge ports from the EC and converts that into + power_supply properties. + config CHARGER_SC2731 tristate "Spreadtrum SC2731 charger driver" depends on MFD_SC27XX_PMIC || COMPILE_TEST diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index dde138bc1591..2fd629dd7068 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o +obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o diff --git a/drivers/power/supply/cros_peripheral_charger.c b/drivers/power/supply/cros_peripheral_charger.c new file mode 100644 index 000000000000..305f10dfc06d --- /dev/null +++ b/drivers/power/supply/cros_peripheral_charger.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Power supply driver for ChromeOS EC based Peripheral Device Charger. + * + * Copyright 2020 Google LLC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-pchg" +#define PCHG_DIR_PREFIX "peripheral" +#define PCHG_DIR_NAME PCHG_DIR_PREFIX "%d" +#define PCHG_DIR_NAME_LENGTH \ + sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS)) +#define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500) + +struct port_data { + int port_number; + char name[PCHG_DIR_NAME_LENGTH]; + struct power_supply *psy; + struct power_supply_desc psy_desc; + int psy_status; + int battery_percentage; + int charge_type; + struct charger_data *charger; + unsigned long last_update; +}; + +struct charger_data { + struct device *dev; + struct cros_ec_dev *ec_dev; + struct cros_ec_device *ec_device; + int num_registered_psy; + struct port_data *ports[EC_PCHG_MAX_PORTS]; + struct notifier_block notifier; +}; + +static enum power_supply_property cros_pchg_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, +}; + +static int cros_pchg_ec_command(const struct charger_data *charger, + unsigned int version, + unsigned int command, + const void *outdata, + unsigned int outsize, + void *indata, + unsigned int insize) +{ + struct cros_ec_dev *ec_dev = charger->ec_dev; + struct cros_ec_command *msg; + int ret; + + msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = version; + msg->command = ec_dev->cmd_offset + command; + msg->outsize = outsize; + msg->insize = insize; + + if (outsize) + memcpy(msg->data, outdata, outsize); + + ret = cros_ec_cmd_xfer_status(charger->ec_device, msg); + if (ret >= 0 && insize) + memcpy(indata, msg->data, insize); + + kfree(msg); + return ret; +} + +static const unsigned int pchg_cmd_version = 1; + +static bool cros_pchg_cmd_ver_check(const struct charger_data *charger) +{ + struct ec_params_get_cmd_versions_v1 req; + struct ec_response_get_cmd_versions rsp; + int ret; + + req.cmd = EC_CMD_PCHG; + ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS, + &req, sizeof(req), &rsp, sizeof(rsp)); + if (ret < 0) { + dev_warn(charger->dev, + "Unable to get versions of EC_CMD_PCHG (err:%d)\n", + ret); + return false; + } + + return !!(rsp.version_mask & BIT(pchg_cmd_version)); +} + +static int cros_pchg_port_count(const struct charger_data *charger) +{ + struct ec_response_pchg_count rsp; + int ret; + + ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT, + NULL, 0, &rsp, sizeof(rsp)); + if (ret < 0) { + dev_warn(charger->dev, + "Unable to get number or ports (err:%d)\n", ret); + return ret; + } + + return rsp.port_count; +} + +static int cros_pchg_get_status(struct port_data *port) +{ + struct charger_data *charger = port->charger; + struct ec_params_pchg req; + struct ec_response_pchg rsp; + struct device *dev = charger->dev; + int old_status = port->psy_status; + int old_percentage = port->battery_percentage; + int ret; + + req.port = port->port_number; + ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG, + &req, sizeof(req), &rsp, sizeof(rsp)); + if (ret < 0) { + dev_err(dev, "Unable to get port.%d status (err:%d)\n", + port->port_number, ret); + return ret; + } + + switch (rsp.state) { + case PCHG_STATE_RESET: + case PCHG_STATE_INITIALIZED: + case PCHG_STATE_ENABLED: + default: + port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN; + port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case PCHG_STATE_DETECTED: + port->psy_status = POWER_SUPPLY_STATUS_CHARGING; + port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case PCHG_STATE_CHARGING: + port->psy_status = POWER_SUPPLY_STATUS_CHARGING; + port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + break; + case PCHG_STATE_FULL: + port->psy_status = POWER_SUPPLY_STATUS_FULL; + port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + port->battery_percentage = rsp.battery_percentage; + + if (port->psy_status != old_status || + port->battery_percentage != old_percentage) + power_supply_changed(port->psy); + + dev_dbg(dev, + "Port %d: state=%d battery=%d%%\n", + port->port_number, rsp.state, rsp.battery_percentage); + + return 0; +} + +static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit) +{ + int ret; + + if (ratelimit && + time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY)) + return 0; + + ret = cros_pchg_get_status(port); + if (ret < 0) + return ret; + + port->last_update = jiffies; + + return ret; +} + +static int cros_pchg_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct port_data *port = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CHARGE_TYPE: + cros_pchg_get_port_status(port, true); + break; + default: + break; + } + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = port->psy_status; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = port->battery_percentage; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = port->charge_type; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cros_pchg_event(const struct charger_data *charger, + unsigned long host_event) +{ + int i; + + for (i = 0; i < charger->num_registered_psy; i++) + cros_pchg_get_port_status(charger->ports[i], false); + + return NOTIFY_OK; +} + +static u32 cros_get_device_event(const struct charger_data *charger) +{ + struct ec_params_device_event req; + struct ec_response_device_event rsp; + struct device *dev = charger->dev; + int ret; + + req.param = EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS; + ret = cros_pchg_ec_command(charger, 0, EC_CMD_DEVICE_EVENT, + &req, sizeof(req), &rsp, sizeof(rsp)); + if (ret < 0) { + dev_warn(dev, "Unable to get device events (err:%d)\n", ret); + return 0; + } + + return rsp.event_mask; +} + +static int cros_ec_notify(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *data) +{ + struct cros_ec_device *ec_dev = (struct cros_ec_device *)data; + u32 host_event = cros_ec_get_host_event(ec_dev); + struct charger_data *charger = + container_of(nb, struct charger_data, notifier); + u32 device_event_mask; + + if (!host_event) + return NOTIFY_DONE; + + if (!(host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_DEVICE))) + return NOTIFY_DONE; + + /* + * todo: Retrieve device event mask in common place + * (e.g. cros_ec_proto.c). + */ + device_event_mask = cros_get_device_event(charger); + if (!(device_event_mask & EC_DEVICE_EVENT_MASK(EC_DEVICE_EVENT_WLC))) + return NOTIFY_DONE; + + return cros_pchg_event(charger, host_event); +} + +static int cros_pchg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); + struct cros_ec_device *ec_device = ec_dev->ec_dev; + struct power_supply_desc *psy_desc; + struct charger_data *charger; + struct power_supply *psy; + struct port_data *port; + struct notifier_block *nb; + int num_ports; + int ret; + int i; + + charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->dev = dev; + charger->ec_dev = ec_dev; + charger->ec_device = ec_device; + + ret = cros_pchg_port_count(charger); + if (ret <= 0) { + /* + * This feature is enabled by the EC and the kernel driver is + * included by default for CrOS devices. Don't need to be loud + * since this error can be normal. + */ + dev_info(dev, "No peripheral charge ports (err:%d)\n", ret); + return -ENODEV; + } + + if (!cros_pchg_cmd_ver_check(charger)) { + dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n", + pchg_cmd_version); + return -EOPNOTSUPP; + } + + num_ports = ret; + if (num_ports > EC_PCHG_MAX_PORTS) { + dev_err(dev, "Too many peripheral charge ports (%d)\n", + num_ports); + return -ENOBUFS; + } + + dev_info(dev, "%d peripheral charge ports found\n", num_ports); + + for (i = 0; i < num_ports; i++) { + struct power_supply_config psy_cfg = {}; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->charger = charger; + port->port_number = i; + snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i); + + psy_desc = &port->psy_desc; + psy_desc->name = port->name; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->get_property = cros_pchg_get_prop; + psy_desc->external_power_changed = NULL; + psy_desc->properties = cros_pchg_props; + psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props); + psy_cfg.drv_data = port; + + psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); + if (IS_ERR(psy)) + return dev_err_probe(dev, PTR_ERR(psy), + "Failed to register power supply\n"); + port->psy = psy; + + charger->ports[charger->num_registered_psy++] = port; + } + + if (!charger->num_registered_psy) + return -ENODEV; + + nb = &charger->notifier; + nb->notifier_call = cros_ec_notify; + ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier, + nb); + if (ret < 0) + dev_err(dev, "Failed to register notifier (err:%d)\n", ret); + + return 0; +} + +static struct platform_driver cros_pchg_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_pchg_probe +}; + +module_platform_driver(cros_pchg_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC peripheral device charger"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 45f53afc46e2..271bd87bff0a 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -4228,6 +4228,7 @@ enum ec_device_event { EC_DEVICE_EVENT_TRACKPAD, EC_DEVICE_EVENT_DSP, EC_DEVICE_EVENT_WIFI, + EC_DEVICE_EVENT_WLC, }; enum ec_device_event_param { @@ -5460,6 +5461,72 @@ struct ec_response_rollback_info { /* Issue AP reset */ #define EC_CMD_AP_RESET 0x0125 +/** + * Get the number of peripheral charge ports + */ +#define EC_CMD_PCHG_COUNT 0x0134 + +#define EC_PCHG_MAX_PORTS 8 + +struct ec_response_pchg_count { + uint8_t port_count; +} __ec_align1; + +/** + * Get the status of a peripheral charge port + */ +#define EC_CMD_PCHG 0x0135 + +struct ec_params_pchg { + uint8_t port; +} __ec_align1; + +struct ec_response_pchg { + uint32_t error; /* enum pchg_error */ + uint8_t state; /* enum pchg_state state */ + uint8_t battery_percentage; + uint8_t unused0; + uint8_t unused1; + /* Fields added in version 1 */ + uint32_t fw_version; + uint32_t dropped_event_count; +} __ec_align2; + +enum pchg_state { + /* Charger is reset and not initialized. */ + PCHG_STATE_RESET = 0, + /* Charger is initialized or disabled. */ + PCHG_STATE_INITIALIZED, + /* Charger is enabled and ready to detect a device. */ + PCHG_STATE_ENABLED, + /* Device is in proximity. */ + PCHG_STATE_DETECTED, + /* Device is being charged. */ + PCHG_STATE_CHARGING, + /* Device is fully charged. It implies DETECTED (& not charging). */ + PCHG_STATE_FULL, + /* In download (a.k.a. firmware update) mode */ + PCHG_STATE_DOWNLOAD, + /* In download mode. Ready for receiving data. */ + PCHG_STATE_DOWNLOADING, + /* Device is ready for data communication. */ + PCHG_STATE_CONNECTED, + /* Put no more entry below */ + PCHG_STATE_COUNT, +}; + +#define EC_PCHG_STATE_TEXT { \ + [PCHG_STATE_RESET] = "RESET", \ + [PCHG_STATE_INITIALIZED] = "INITIALIZED", \ + [PCHG_STATE_ENABLED] = "ENABLED", \ + [PCHG_STATE_DETECTED] = "DETECTED", \ + [PCHG_STATE_CHARGING] = "CHARGING", \ + [PCHG_STATE_FULL] = "FULL", \ + [PCHG_STATE_DOWNLOAD] = "DOWNLOAD", \ + [PCHG_STATE_DOWNLOADING] = "DOWNLOADING", \ + [PCHG_STATE_CONNECTED] = "CONNECTED", \ + } + /*****************************************************************************/ /* Voltage regulator controls */ From ad1abe476995d97bfe7546ea91bb4f3dcdfbf3ab Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Thu, 1 Jul 2021 23:05:16 +0100 Subject: [PATCH 054/177] power: supply: cw2015: use dev_err_probe to allow deferred probe Deal with deferred probe using dev_err_probe so the error is handled and avoid logging lots probe defer information like the following: [ 9.125121] cw2015 4-0062: Failed to register power supply [ 9.211131] cw2015 4-0062: Failed to register power supply Fixes: b4c7715c10c1 ("power: supply: add CellWise cw2015 fuel gauge driver") Signed-off-by: Peter Robinson Reviewed-by: Javier Martinez Canillas Signed-off-by: Sebastian Reichel --- drivers/power/supply/cw2015_battery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index d110597746b0..091868e9e9e8 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -679,7 +679,9 @@ static int cw_bat_probe(struct i2c_client *client) &cw2015_bat_desc, &psy_cfg); if (IS_ERR(cw_bat->rk_bat)) { - dev_err(cw_bat->dev, "Failed to register power supply\n"); + /* try again if this happens */ + dev_err_probe(&client->dev, PTR_ERR(cw_bat->rk_bat), + "Failed to register power supply\n"); return PTR_ERR(cw_bat->rk_bat); } From f020e4d0b4016f5592d082cc3a1db430c567c4dc Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 19 Jul 2021 11:34:29 +0100 Subject: [PATCH 055/177] regulator: Fix a couple of spelling mistakes in Kconfig There are a couple of spelling mistakes in the Kconfig text. Fix them. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20210719103429.15544-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6562d4c243b0..c63d5faa883c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1044,7 +1044,7 @@ config REGULATOR_RT6160 help This adds support for voltage regulator in Richtek RT6160. This device automatically change voltage output mode from - Buck or Boost. The mode transistion depend on the input source voltage. + Buck or Boost. The mode transition depend on the input source voltage. The wide output range is from 2025mV to 5200mV and can be used on most common application scenario. @@ -1053,7 +1053,7 @@ config REGULATOR_RT6245 depends on I2C select REGMAP_I2C help - This adds supprot for Richtek RT6245 voltage regulator. + This adds support for Richtek RT6245 voltage regulator. It can support up to 14A output current and adjustable output voltage from 0.4375V to 1.3875V, per step 12.5mV. From e0a6512d29126901dd16dfede314616b57ec8210 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 Jul 2021 10:48:40 +0300 Subject: [PATCH 056/177] spi: pxa2xx: Convert reset_sccr1() to use pxa2xx_spi_update() Convert reset_sccr1() to use pxa2xx_spi_update(). It will help for further improvements. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210719074842.36060-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 974e30744b83..7c4c8179a329 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -594,24 +594,22 @@ static int u32_reader(struct driver_data *drv_data) static void reset_sccr1(struct driver_data *drv_data) { - struct chip_data *chip = - spi_get_ctldata(drv_data->controller->cur_msg->spi); - u32 sccr1_reg; + struct chip_data *chip = spi_get_ctldata(drv_data->controller->cur_msg->spi); + u32 mask = drv_data->int_cr1; - sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1; switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - sccr1_reg &= ~QUARK_X1000_SSCR1_RFT; + mask |= QUARK_X1000_SSCR1_RFT; break; case CE4100_SSP: - sccr1_reg &= ~CE4100_SSCR1_RFT; + mask |= CE4100_SSCR1_RFT; break; default: - sccr1_reg &= ~SSCR1_RFT; + mask |= SSCR1_RFT; break; } - sccr1_reg |= chip->threshold; - pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); + + pxa2xx_spi_update(drv_data, SSCR1, mask, chip->threshold); } static void int_stop_and_reset(struct driver_data *drv_data) From cdcb26ce747a5ec665a98fd6c303248a12418140 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 Jul 2021 10:48:41 +0300 Subject: [PATCH 057/177] spi: pxa2xx: Reset DMA bits in CR1 in reset_sccr1() In order to allow reset_sccr1() to be reused in DMA paths, reset DMA bits in CR1 in this function. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210719074842.36060-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 7c4c8179a329..833eb52ed305 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -595,7 +595,7 @@ static int u32_reader(struct driver_data *drv_data) static void reset_sccr1(struct driver_data *drv_data) { struct chip_data *chip = spi_get_ctldata(drv_data->controller->cur_msg->spi); - u32 mask = drv_data->int_cr1; + u32 mask = drv_data->int_cr1 | drv_data->dma_cr1; switch (drv_data->ssp_type) { case QUARK_X1000_SSP: From 3bbdc083262dc082e5c8e7b0646faf8f4ef894dd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 Jul 2021 10:48:42 +0300 Subject: [PATCH 058/177] spi: pxa2xx: Reuse int_stop_and_reset() in couple of places Reuse int_stop_and_reset() in couple of places. While at it, change the order of the int_stop_and_reset() and pxa2xx_spi_off() to be in align with the similar flow in int_error_stop(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210719074842.36060-3-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 833eb52ed305..19a2d1ea7d42 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -722,11 +722,8 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) static void handle_bad_msg(struct driver_data *drv_data) { + int_stop_and_reset(drv_data); pxa2xx_spi_off(drv_data); - clear_SSCR1_bits(drv_data, drv_data->int_cr1); - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); - write_SSSR_CS(drv_data, drv_data->clear_sr); dev_err(drv_data->ssp->dev, "bad message state in interrupt handler\n"); } @@ -1154,13 +1151,10 @@ static void pxa2xx_spi_handle_err(struct spi_controller *controller, { struct driver_data *drv_data = spi_controller_get_devdata(controller); + int_stop_and_reset(drv_data); + /* Disable the SSP */ pxa2xx_spi_off(drv_data); - /* Clear and disable interrupts and service requests */ - write_SSSR_CS(drv_data, drv_data->clear_sr); - clear_SSCR1_bits(drv_data, drv_data->int_cr1 | drv_data->dma_cr1); - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); /* * Stop the DMA if running. Note DMA callback handler may have unset From bd9616996bb8cd6fbceedf00f1aa72fd9a845519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 16 Jul 2021 19:39:27 +0200 Subject: [PATCH 059/177] spi: imx: Simplify logic in spi_imx_push() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For each usage of fifo_words it is clear if ->dynamic_burst is true or not. This can be used to simplify the function a bit. Signed-off-by: Uwe Kleine-König Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/20210716173927.2050620-1-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 4aee3db6d6df..593b63be73de 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1038,12 +1038,8 @@ static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits) static void spi_imx_push(struct spi_imx_data *spi_imx) { - unsigned int burst_len, fifo_words; + unsigned int burst_len; - if (spi_imx->dynamic_burst) - fifo_words = 4; - else - fifo_words = spi_imx_bytes_per_word(spi_imx->bits_per_word); /* * Reload the FIFO when the remaining bytes to be transferred in the * current burst is 0. This only applies when bits_per_word is a @@ -1062,7 +1058,7 @@ static void spi_imx_push(struct spi_imx_data *spi_imx) spi_imx->remainder = burst_len; } else { - spi_imx->remainder = fifo_words; + spi_imx->remainder = spi_imx_bytes_per_word(spi_imx->bits_per_word); } } @@ -1070,8 +1066,7 @@ static void spi_imx_push(struct spi_imx_data *spi_imx) if (!spi_imx->count) break; if (spi_imx->dynamic_burst && - spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, - fifo_words)) + spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, 4)) break; spi_imx->tx(spi_imx); spi_imx->txfifo++; From 34d34a56a5ea1e54a5af4f34c6ac9df724129351 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 08:39:14 -0500 Subject: [PATCH 060/177] spi: fsi: Reduce max transfer size to 8 bytes Security changes have forced the SPI controllers to be limited to 8 byte reads. Refactor the sequencing to just handle 8 bytes at a time. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20210716133915.14697-2-eajames@linux.ibm.com Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-fsi.c | 125 ++++++++---------------------------------- 1 file changed, 22 insertions(+), 103 deletions(-) diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index 87f8829c3995..829770b8ec74 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -25,16 +25,11 @@ #define SPI_FSI_BASE 0x70000 #define SPI_FSI_INIT_TIMEOUT_MS 1000 -#define SPI_FSI_MAX_XFR_SIZE 2048 -#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 8 +#define SPI_FSI_MAX_RX_SIZE 8 +#define SPI_FSI_MAX_TX_SIZE 40 #define SPI_FSI_ERROR 0x0 #define SPI_FSI_COUNTER_CFG 0x1 -#define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32) -#define SPI_FSI_COUNTER_CFG_N2_RX BIT_ULL(8) -#define SPI_FSI_COUNTER_CFG_N2_TX BIT_ULL(9) -#define SPI_FSI_COUNTER_CFG_N2_IMPLICIT BIT_ULL(10) -#define SPI_FSI_COUNTER_CFG_N2_RELOAD BIT_ULL(11) #define SPI_FSI_CFG1 0x2 #define SPI_FSI_CLOCK_CFG 0x3 #define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32) @@ -76,8 +71,6 @@ struct fsi_spi { struct device *dev; /* SPI controller device */ struct fsi_device *fsi; /* FSI2SPI CFAM engine device */ u32 base; - size_t max_xfr_size; - bool restricted; }; struct fsi_spi_sequence { @@ -241,7 +234,7 @@ static int fsi_spi_reset(struct fsi_spi *ctx) return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL); } -static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) +static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) { /* * Add the next byte of instruction to the 8-byte sequence register. @@ -251,8 +244,6 @@ static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) */ seq->data |= (u64)val << seq->bit; seq->bit -= 8; - - return ((64 - seq->bit) / 8) - 2; } static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq) @@ -261,71 +252,11 @@ static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq) seq->data = 0ULL; } -static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, - struct fsi_spi_sequence *seq, - struct spi_transfer *transfer) -{ - int loops; - int idx; - int rc; - u8 val = 0; - u8 len = min(transfer->len, 8U); - u8 rem = transfer->len % len; - - loops = transfer->len / len; - - if (transfer->tx_buf) { - val = SPI_FSI_SEQUENCE_SHIFT_OUT(len); - idx = fsi_spi_sequence_add(seq, val); - - if (rem) - rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem); - } else if (transfer->rx_buf) { - val = SPI_FSI_SEQUENCE_SHIFT_IN(len); - idx = fsi_spi_sequence_add(seq, val); - - if (rem) - rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem); - } else { - return -EINVAL; - } - - if (ctx->restricted && loops > 1) { - dev_warn(ctx->dev, - "Transfer too large; no branches permitted.\n"); - return -EINVAL; - } - - if (loops > 1) { - u64 cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1); - - fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx)); - - if (transfer->rx_buf) - cfg |= SPI_FSI_COUNTER_CFG_N2_RX | - SPI_FSI_COUNTER_CFG_N2_TX | - SPI_FSI_COUNTER_CFG_N2_IMPLICIT | - SPI_FSI_COUNTER_CFG_N2_RELOAD; - - rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg); - if (rc) - return rc; - } else { - fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL); - } - - if (rem) - fsi_spi_sequence_add(seq, rem); - - return 0; -} - static int fsi_spi_transfer_data(struct fsi_spi *ctx, struct spi_transfer *transfer) { int rc = 0; u64 status = 0ULL; - u64 cfg = 0ULL; if (transfer->tx_buf) { int nb; @@ -363,16 +294,6 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, u64 in = 0ULL; u8 *rx = transfer->rx_buf; - rc = fsi_spi_read_reg(ctx, SPI_FSI_COUNTER_CFG, &cfg); - if (rc) - return rc; - - if (cfg & SPI_FSI_COUNTER_CFG_N2_IMPLICIT) { - rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, 0); - if (rc) - return rc; - } - while (transfer->len > recv) { do { rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, @@ -439,6 +360,10 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx) } } while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE)); + rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL); + if (rc) + return rc; + rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg); if (rc) return rc; @@ -459,6 +384,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, { int rc; u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1); + unsigned int len; struct spi_transfer *transfer; struct fsi_spi *ctx = spi_controller_get_devdata(ctlr); @@ -471,8 +397,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, struct spi_transfer *next = NULL; /* Sequencer must do shift out (tx) first. */ - if (!transfer->tx_buf || - transfer->len > (ctx->max_xfr_size + 8)) { + if (!transfer->tx_buf || transfer->len > SPI_FSI_MAX_TX_SIZE) { rc = -EINVAL; goto error; } @@ -486,9 +411,13 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, fsi_spi_sequence_init(&seq); fsi_spi_sequence_add(&seq, seq_slave); - rc = fsi_spi_sequence_transfer(ctx, &seq, transfer); - if (rc) - goto error; + len = transfer->len; + while (len > 8) { + fsi_spi_sequence_add(&seq, + SPI_FSI_SEQUENCE_SHIFT_OUT(8)); + len -= 8; + } + fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SHIFT_OUT(len)); if (!list_is_last(&transfer->transfer_list, &mesg->transfers)) { @@ -496,7 +425,9 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, /* Sequencer can only do shift in (rx) after tx. */ if (next->rx_buf) { - if (next->len > ctx->max_xfr_size) { + u8 shift; + + if (next->len > SPI_FSI_MAX_RX_SIZE) { rc = -EINVAL; goto error; } @@ -504,10 +435,8 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n", next->len); - rc = fsi_spi_sequence_transfer(ctx, &seq, - next); - if (rc) - goto error; + shift = SPI_FSI_SEQUENCE_SHIFT_IN(next->len); + fsi_spi_sequence_add(&seq, shift); } else { next = NULL; } @@ -541,9 +470,7 @@ error: static size_t fsi_spi_max_transfer_size(struct spi_device *spi) { - struct fsi_spi *ctx = spi_controller_get_devdata(spi->controller); - - return ctx->max_xfr_size; + return SPI_FSI_MAX_RX_SIZE; } static int fsi_spi_probe(struct device *dev) @@ -582,14 +509,6 @@ static int fsi_spi_probe(struct device *dev) ctx->fsi = fsi; ctx->base = base + SPI_FSI_BASE; - if (of_device_is_compatible(np, "ibm,fsi2spi-restricted")) { - ctx->restricted = true; - ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE_RESTRICTED; - } else { - ctx->restricted = false; - ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE; - } - rc = devm_spi_register_controller(dev, ctlr); if (rc) spi_controller_put(ctlr); From 2b2d4dfca4e7cb6de70985b1579a6c08c027b8c9 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 08:39:15 -0500 Subject: [PATCH 061/177] dt-bindings: fsi: Remove ibm,fsi2spi-restricted compatible Remove this compatible string from the FSI SPI controller documentation, since the security restrictions have been universally applied to the controllers. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20210716133915.14697-3-eajames@linux.ibm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml b/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml index e425278653f5..e2ca0b000471 100644 --- a/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml +++ b/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml @@ -19,7 +19,6 @@ properties: compatible: enum: - ibm,fsi2spi - - ibm,fsi2spi-restricted reg: items: From 4c922fd3d6fa4d51e1f5bb845548cbefd5de3d14 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 16 Jul 2021 16:03:55 +0800 Subject: [PATCH 062/177] regulator: rtq2134: Add binding document for Richtek RTQ2134 SubPMIC Add binding document for Richtek RTQ2134 SubPMIC. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1626422636-29458-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- .../regulator/richtek,rtq2134-regulator.yaml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/richtek,rtq2134-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/richtek,rtq2134-regulator.yaml b/Documentation/devicetree/bindings/regulator/richtek,rtq2134-regulator.yaml new file mode 100644 index 000000000000..3f47e8e6c4fd --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/richtek,rtq2134-regulator.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/richtek,rtq2134-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RTQ2134 SubPMIC Regulator + +maintainers: + - ChiYuan Huang + +description: | + The RTQ2134 is a multi-phase, programmable power management IC that + integrates with four high efficient, synchronous step-down converter cores. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RTQ2134-QA/DSQ2134-QA-01.pdf + +properties: + compatible: + enum: + - richtek,rtq2134 + + reg: + maxItems: 1 + + regulators: + type: object + + patternProperties: + "^buck[1-3]$": + type: object + $ref: regulator.yaml# + description: | + regulator description for buck[1-3]. + + properties: + richtek,use-vsel-dvs: + type: boolean + description: | + If specified, buck will listen to 'vsel' pin for dvs config. + Else, use dvs0 voltage by default. + + richtek,uv-shutdown: + type: boolean + description: | + If specified, use shutdown as UV action. Else, hiccup by default. + + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtq2134@18 { + compatible = "richtek,rtq2134"; + reg = <0x18>; + + regulators { + buck1 { + regulator-name = "rtq2134-buck1"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1850000>; + regulator-always-on; + richtek,use-vsel-dvs; + regulator-state-mem { + regulator-suspend-min-microvolt = <550000>; + regulator-suspend-max-microvolt = <550000>; + }; + }; + buck2 { + regulator-name = "rtq2134-buck2"; + regulator-min-microvolt = <1120000>; + regulator-max-microvolt = <1120000>; + regulator-always-on; + richtek,use-vsel-dvs; + regulator-state-mem { + regulator-suspend-min-microvolt = <1120000>; + regulator-suspend-max-microvolt = <1120000>; + }; + }; + buck3 { + regulator-name = "rtq2134-buck3"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <600000>; + regulator-always-on; + richtek,use-vsel-dvs; + regulator-state-mem { + regulator-suspend-min-microvolt = <600000>; + regulator-suspend-max-microvolt = <600000>; + }; + }; + }; + }; + }; From 0555d41497de66d2f07ae36dcb46a32f0ff90d8d Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 16 Jul 2021 16:03:56 +0800 Subject: [PATCH 063/177] regulator: rtq2134: Add support for Richtek RTQ2134 SubPMIC This adds support for Richtek RTQ2134 SubPMIC. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1626422636-29458-2-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 11 + drivers/regulator/Makefile | 1 + drivers/regulator/rtq2134-regulator.c | 373 ++++++++++++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 drivers/regulator/rtq2134-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c63d5faa883c..4fd13b06231f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1057,6 +1057,17 @@ config REGULATOR_RT6245 It can support up to 14A output current and adjustable output voltage from 0.4375V to 1.3875V, per step 12.5mV. +config REGULATOR_RTQ2134 + tristate "Richtek RTQ2134 SubPMIC Regulator" + depends on I2C + select REGMAP_I2C + help + This driver adds support for RTQ2134 SubPMIC regulators. + The RTQ2134 is a multi-phase, programmable power management IC that + integrate with four high efficient, synchronous step-down converter + cores. It features wide output voltage range and the capability to + configure the corresponding power stages. + config REGULATOR_RTMV20 tristate "Richtek RTMV20 Laser Diode Regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 102b2a0069a2..9e382b50a5ef 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o +obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o diff --git a/drivers/regulator/rtq2134-regulator.c b/drivers/regulator/rtq2134-regulator.c new file mode 100644 index 000000000000..e09419cc2793 --- /dev/null +++ b/drivers/regulator/rtq2134-regulator.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include + +enum { + RTQ2134_IDX_BUCK1 = 0, + RTQ2134_IDX_BUCK2, + RTQ2134_IDX_BUCK3, + RTQ2134_IDX_MAX +}; + +#define RTQ2134_AUTO_MODE 0 +#define RTQ2134_FCCM_MODE 1 + +#define RTQ2134_BUCK_DVS0_CTRL 0 +#define RTQ2134_BUCK_VSEL_CTRL 2 + +#define RTQ2134_REG_IO_CHIPNAME 0x01 +#define RTQ2134_REG_FLT_RECORDTEMP 0x13 +#define RTQ2134_REG_FLT_RECORDBUCK(_id) (0x14 + (_id)) +#define RTQ2134_REG_FLT_BUCKCTRL(_id) (0x37 + (_id)) +#define RTQ2134_REG_BUCK1_CFG0 0x42 +#define RTQ2134_REG_BUCK1_DVS0CFG1 0x48 +#define RTQ2134_REG_BUCK1_DVS0CFG0 0x49 +#define RTQ2134_REG_BUCK1_DVS1CFG1 0x4A +#define RTQ2134_REG_BUCK1_DVS1CFG0 0x4B +#define RTQ2134_REG_BUCK1_DVSCFG 0x52 +#define RTQ2134_REG_BUCK1_RSPCFG 0x54 +#define RTQ2134_REG_BUCK2_CFG0 0x5F +#define RTQ2134_REG_BUCK2_DVS0CFG1 0x62 +#define RTQ2134_REG_BUCK2_DVS0CFG0 0x63 +#define RTQ2134_REG_BUCK2_DVS1CFG1 0x64 +#define RTQ2134_REG_BUCK2_DVS1CFG0 0x65 +#define RTQ2134_REG_BUCK2_DVSCFG 0x6C +#define RTQ2134_REG_BUCK2_RSPCFG 0x6E +#define RTQ2134_REG_BUCK3_CFG0 0x79 +#define RTQ2134_REG_BUCK3_DVS0CFG1 0x7C +#define RTQ2134_REG_BUCK3_DVS0CFG0 0x7D +#define RTQ2134_REG_BUCK3_DVS1CFG1 0x7E +#define RTQ2134_REG_BUCK3_DVS1CFG0 0x7F +#define RTQ2134_REG_BUCK3_DVSCFG 0x86 +#define RTQ2134_REG_BUCK3_RSPCFG 0x88 +#define RTQ2134_REG_BUCK3_SLEWCTRL 0x89 + +#define RTQ2134_VOUT_MAXNUM 256 +#define RTQ2134_VOUT_MASK 0xFF +#define RTQ2134_VOUTEN_MASK BIT(0) +#define RTQ2134_ACTDISCHG_MASK BIT(0) +#define RTQ2134_RSPUP_MASK GENMASK(6, 4) +#define RTQ2134_FCCM_MASK BIT(5) +#define RTQ2134_UVHICCUP_MASK BIT(3) +#define RTQ2134_BUCKDVS_CTRL_MASK GENMASK(1, 0) +#define RTQ2134_CHIPOT_MASK BIT(2) +#define RTQ2134_BUCKOV_MASK BIT(5) +#define RTQ2134_BUCKUV_MASK BIT(4) + +struct rtq2134_regulator_desc { + struct regulator_desc desc; + /* Extension for proprietary register and mask */ + unsigned int mode_reg; + unsigned int mode_mask; + unsigned int suspend_enable_reg; + unsigned int suspend_enable_mask; + unsigned int suspend_vsel_reg; + unsigned int suspend_vsel_mask; + unsigned int suspend_mode_reg; + unsigned int suspend_mode_mask; + unsigned int dvs_ctrl_reg; +}; + +static int rtq2134_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + unsigned int val; + + if (mode == REGULATOR_MODE_NORMAL) + val = RTQ2134_AUTO_MODE; + else if (mode == REGULATOR_MODE_FAST) + val = RTQ2134_FCCM_MODE; + else + return -EINVAL; + + val <<= ffs(desc->mode_mask) - 1; + return regmap_update_bits(rdev->regmap, desc->mode_reg, desc->mode_mask, + val); +} + +static unsigned int rtq2134_buck_get_mode(struct regulator_dev *rdev) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + unsigned int mode; + int ret; + + ret = regmap_read(rdev->regmap, desc->mode_reg, &mode); + if (ret) + return ret; + + if (mode & desc->mode_mask) + return REGULATOR_MODE_FAST; + return REGULATOR_MODE_NORMAL; +} + +static int rtq2134_buck_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + int sel; + + sel = regulator_map_voltage_linear_range(rdev, uV, uV); + if (sel < 0) + return sel; + + sel <<= ffs(desc->suspend_vsel_mask) - 1; + + return regmap_update_bits(rdev->regmap, desc->suspend_vsel_reg, + desc->suspend_vsel_mask, sel); +} + +static int rtq2134_buck_set_suspend_enable(struct regulator_dev *rdev) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + unsigned int val = desc->suspend_enable_mask; + + return regmap_update_bits(rdev->regmap, desc->suspend_enable_reg, + desc->suspend_enable_mask, val); +} + +static int rtq2134_buck_set_suspend_disable(struct regulator_dev *rdev) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + + return regmap_update_bits(rdev->regmap, desc->suspend_enable_reg, + desc->suspend_enable_mask, 0); +} + +static int rtq2134_buck_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct rtq2134_regulator_desc *desc = + (struct rtq2134_regulator_desc *)rdev->desc; + unsigned int val; + + if (mode == REGULATOR_MODE_NORMAL) + val = RTQ2134_AUTO_MODE; + else if (mode == REGULATOR_MODE_FAST) + val = RTQ2134_FCCM_MODE; + else + return -EINVAL; + + val <<= ffs(desc->suspend_mode_mask) - 1; + return regmap_update_bits(rdev->regmap, desc->suspend_mode_reg, + desc->suspend_mode_mask, val); +} + +static int rtq2134_buck_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + int rid = rdev_get_id(rdev); + unsigned int chip_error, buck_error, events = 0; + int ret; + + ret = regmap_read(rdev->regmap, RTQ2134_REG_FLT_RECORDTEMP, + &chip_error); + if (ret) { + dev_err(&rdev->dev, "Failed to get chip error flag\n"); + return ret; + } + + ret = regmap_read(rdev->regmap, RTQ2134_REG_FLT_RECORDBUCK(rid), + &buck_error); + if (ret) { + dev_err(&rdev->dev, "Failed to get buck error flag\n"); + return ret; + } + + if (chip_error & RTQ2134_CHIPOT_MASK) + events |= REGULATOR_ERROR_OVER_TEMP; + + if (buck_error & RTQ2134_BUCKUV_MASK) + events |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (buck_error & RTQ2134_BUCKOV_MASK) + events |= REGULATOR_ERROR_REGULATION_OUT; + + *flags = events; + return 0; +} + +static const struct regulator_ops rtq2134_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_ramp_delay = regulator_set_ramp_delay_regmap, + .set_mode = rtq2134_buck_set_mode, + .get_mode = rtq2134_buck_get_mode, + .set_suspend_voltage = rtq2134_buck_set_suspend_voltage, + .set_suspend_enable = rtq2134_buck_set_suspend_enable, + .set_suspend_disable = rtq2134_buck_set_suspend_disable, + .set_suspend_mode = rtq2134_buck_set_suspend_mode, + .get_error_flags = rtq2134_buck_get_error_flags, +}; + +static const struct linear_range rtq2134_buck_vout_ranges[] = { + REGULATOR_LINEAR_RANGE(300000, 0, 200, 5000), + REGULATOR_LINEAR_RANGE(1310000, 201, 255, 10000) +}; + +static unsigned int rtq2134_buck_of_map_mode(unsigned int mode) +{ + switch (mode) { + case RTQ2134_AUTO_MODE: + return REGULATOR_MODE_NORMAL; + case RTQ2134_FCCM_MODE: + return REGULATOR_MODE_FAST; + } + + return REGULATOR_MODE_INVALID; +} + +static int rtq2134_buck_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct rtq2134_regulator_desc *rdesc = + (struct rtq2134_regulator_desc *)desc; + int rid = desc->id; + bool uv_shutdown, vsel_dvs; + unsigned int val; + int ret; + + vsel_dvs = of_property_read_bool(np, "richtek,use-vsel-dvs"); + if (vsel_dvs) + val = RTQ2134_BUCK_VSEL_CTRL; + else + val = RTQ2134_BUCK_DVS0_CTRL; + + ret = regmap_update_bits(cfg->regmap, rdesc->dvs_ctrl_reg, + RTQ2134_BUCKDVS_CTRL_MASK, val); + if (ret) + return ret; + + uv_shutdown = of_property_read_bool(np, "richtek,uv-shutdown"); + if (uv_shutdown) + val = 0; + else + val = RTQ2134_UVHICCUP_MASK; + + return regmap_update_bits(cfg->regmap, RTQ2134_REG_FLT_BUCKCTRL(rid), + RTQ2134_UVHICCUP_MASK, val); +} + +static const unsigned int rtq2134_buck_ramp_delay_table[] = { + 0, 16000, 0, 8000, 4000, 2000, 1000, 500 +}; + +#define RTQ2134_BUCK_DESC(_id) { \ + .desc = { \ + .name = "rtq2134_buck" #_id, \ + .of_match = of_match_ptr("buck" #_id), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = RTQ2134_IDX_BUCK##_id, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .ops = &rtq2134_buck_ops, \ + .n_voltages = RTQ2134_VOUT_MAXNUM, \ + .linear_ranges = rtq2134_buck_vout_ranges, \ + .n_linear_ranges = ARRAY_SIZE(rtq2134_buck_vout_ranges), \ + .vsel_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG1, \ + .vsel_mask = RTQ2134_VOUT_MASK, \ + .enable_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG0, \ + .enable_mask = RTQ2134_VOUTEN_MASK, \ + .active_discharge_reg = RTQ2134_REG_BUCK##_id##_CFG0, \ + .active_discharge_mask = RTQ2134_ACTDISCHG_MASK, \ + .ramp_reg = RTQ2134_REG_BUCK##_id##_RSPCFG, \ + .ramp_mask = RTQ2134_RSPUP_MASK, \ + .ramp_delay_table = rtq2134_buck_ramp_delay_table, \ + .n_ramp_values = ARRAY_SIZE(rtq2134_buck_ramp_delay_table), \ + .of_map_mode = rtq2134_buck_of_map_mode, \ + .of_parse_cb = rtq2134_buck_of_parse_cb, \ + }, \ + .mode_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG0, \ + .mode_mask = RTQ2134_FCCM_MASK, \ + .suspend_mode_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG0, \ + .suspend_mode_mask = RTQ2134_FCCM_MASK, \ + .suspend_enable_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG0, \ + .suspend_enable_mask = RTQ2134_VOUTEN_MASK, \ + .suspend_vsel_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG1, \ + .suspend_vsel_mask = RTQ2134_VOUT_MASK, \ + .dvs_ctrl_reg = RTQ2134_REG_BUCK##_id##_DVSCFG, \ +} + +static const struct rtq2134_regulator_desc rtq2134_regulator_descs[] = { + RTQ2134_BUCK_DESC(1), + RTQ2134_BUCK_DESC(2), + RTQ2134_BUCK_DESC(3) +}; + +static bool rtq2134_is_accissible_reg(struct device *dev, unsigned int reg) +{ + if (RTQ2134_REG_IO_CHIPNAME <= reg && reg <= RTQ2134_REG_BUCK3_SLEWCTRL) + return true; + return false; +} + +static const struct regmap_config rtq2134_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RTQ2134_REG_BUCK3_SLEWCTRL, + + .readable_reg = rtq2134_is_accissible_reg, + .writeable_reg = rtq2134_is_accissible_reg, +}; + +static int rtq2134_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + struct regulator_dev *rdev; + struct regulator_config regulator_cfg= {}; + int i; + + regmap = devm_regmap_init_i2c(i2c, &rtq2134_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&i2c->dev, "Failed to allocate regmap\n"); + return PTR_ERR(regmap); + } + + regulator_cfg.dev = &i2c->dev; + regulator_cfg.regmap = regmap; + for (i = 0; i < ARRAY_SIZE(rtq2134_regulator_descs); i++) { + rdev = devm_regulator_register(&i2c->dev, + &rtq2134_regulator_descs[i].desc, + ®ulator_cfg); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to init %d regulator\n", i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rtq2134_device_tables[] = { + { .compatible = "richtek,rtq2134", }, + {} +}; +MODULE_DEVICE_TABLE(of, rtq2134_device_tables); + +static struct i2c_driver rtq2134_driver = { + .driver = { + .name = "rtq2134", + .of_match_table = rtq2134_device_tables, + }, + .probe_new = rtq2134_probe, +}; +module_i2c_driver(rtq2134_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Richtek RTQ2134 Regulator Driver"); +MODULE_LICENSE("GPL v2"); From d0f95e6496a974a890df5eda65ffaee66ab0dc73 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 21 Jul 2021 11:57:16 -0500 Subject: [PATCH 064/177] regulator: fixed: use dev_err_probe for register Instead of returning error directly, use dev_err_probe. This avoids messages in the dmesg log for devices which will be probed again later. Signed-off-by: Chris Morgan Link: https://lore.kernel.org/r/20210721165716.19915-1-macroalpha82@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 39284610a536..599ad201dca7 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -287,8 +287,9 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, &cfg); if (IS_ERR(drvdata->dev)) { - ret = PTR_ERR(drvdata->dev); - dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + ret = dev_err_probe(&pdev->dev, PTR_ERR(drvdata->dev), + "Failed to register regulator: %ld\n", + PTR_ERR(drvdata->dev)); return ret; } From e3aa9acc71778266cc4743217ff1a1a53caf15d6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jul 2021 15:15:20 +0300 Subject: [PATCH 065/177] spi: pxa2xx: Adapt reset_sccr1() to the case when no message available In some cases reset_sccr1() can be called when no message available. This means that there is no associated chip to receive that message and hence no threshold needs to be set. Adapt the function to such cases. Fixes: 3bbdc083262d ("spi: pxa2xx: Reuse int_stop_and_reset() in couple of places") Reported-by: Dan Carpenter Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210721121520.62605-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 19a2d1ea7d42..1573f6d8eb48 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -594,8 +594,15 @@ static int u32_reader(struct driver_data *drv_data) static void reset_sccr1(struct driver_data *drv_data) { - struct chip_data *chip = spi_get_ctldata(drv_data->controller->cur_msg->spi); - u32 mask = drv_data->int_cr1 | drv_data->dma_cr1; + u32 mask = drv_data->int_cr1 | drv_data->dma_cr1, threshold; + struct chip_data *chip; + + if (drv_data->controller->cur_msg) { + chip = spi_get_ctldata(drv_data->controller->cur_msg->spi); + threshold = chip->threshold; + } else { + threshold = 0; + } switch (drv_data->ssp_type) { case QUARK_X1000_SSP: @@ -609,7 +616,7 @@ static void reset_sccr1(struct driver_data *drv_data) break; } - pxa2xx_spi_update(drv_data, SSCR1, mask, chip->threshold); + pxa2xx_spi_update(drv_data, SSCR1, mask, threshold); } static void int_stop_and_reset(struct driver_data *drv_data) From ccb2a74eec211c368ddbe3eaec4a20292e431095 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Jul 2021 12:22:41 +0200 Subject: [PATCH 066/177] regulator: hi6421v600: use lowercase for ldo As lowercase is generally preferred for node names, while it is not too late, change the LDO DT properties to lower case. Suggested-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/395510cffeb39aebd1763cc5de1cb00a2c40e461.1627121912.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/hi6421v600-regulator.c | 36 ++++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/regulator/hi6421v600-regulator.c b/drivers/regulator/hi6421v600-regulator.c index a8501e8720d0..1d9723ec9731 100644 --- a/drivers/regulator/hi6421v600-regulator.c +++ b/drivers/regulator/hi6421v600-regulator.c @@ -73,14 +73,14 @@ static const unsigned int ldo34_voltages[] = { */ #define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \ odelay, etime, ecomask, ecoamp) \ - [HI6421V600_##_id] = { \ + [hi6421v600_##_id] = { \ .desc = { \ .name = #_id, \ .of_match = of_match_ptr(#_id), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &hi6421_spmi_ldo_rops, \ .type = REGULATOR_VOLTAGE, \ - .id = HI6421V600_##_id, \ + .id = hi6421v600_##_id, \ .owner = THIS_MODULE, \ .volt_table = vtable, \ .n_voltages = ARRAY_SIZE(vtable), \ @@ -185,46 +185,46 @@ static const struct regulator_ops hi6421_spmi_ldo_rops = { /* HI6421v600 regulators with known registers */ enum hi6421_spmi_regulator_id { - HI6421V600_LDO3, - HI6421V600_LDO4, - HI6421V600_LDO9, - HI6421V600_LDO15, - HI6421V600_LDO16, - HI6421V600_LDO17, - HI6421V600_LDO33, - HI6421V600_LDO34, + hi6421v600_ldo3, + hi6421v600_ldo4, + hi6421v600_ldo9, + hi6421v600_ldo15, + hi6421v600_ldo16, + hi6421v600_ldo17, + hi6421v600_ldo33, + hi6421v600_ldo34, }; static struct hi6421_spmi_reg_info regulator_info[] = { - HI6421V600_LDO(LDO3, ldo3_voltages, + HI6421V600_LDO(ldo3, ldo3_voltages, 0x16, 0x01, 0x51, 20000, 120, 0, 0), - HI6421V600_LDO(LDO4, ldo4_voltages, + HI6421V600_LDO(ldo4, ldo4_voltages, 0x17, 0x01, 0x52, 20000, 120, 0x10, 10000), - HI6421V600_LDO(LDO9, ldo9_voltages, + HI6421V600_LDO(ldo9, ldo9_voltages, 0x1c, 0x01, 0x57, 20000, 360, 0x10, 10000), - HI6421V600_LDO(LDO15, ldo15_voltages, + HI6421V600_LDO(ldo15, ldo15_voltages, 0x21, 0x01, 0x5c, 20000, 360, 0x10, 10000), - HI6421V600_LDO(LDO16, ldo15_voltages, + HI6421V600_LDO(ldo16, ldo15_voltages, 0x22, 0x01, 0x5d, 20000, 360, 0x10, 10000), - HI6421V600_LDO(LDO17, ldo17_voltages, + HI6421V600_LDO(ldo17, ldo17_voltages, 0x23, 0x01, 0x5e, 20000, 120, 0x10, 10000), - HI6421V600_LDO(LDO33, ldo17_voltages, + HI6421V600_LDO(ldo33, ldo17_voltages, 0x32, 0x01, 0x6d, 20000, 120, 0, 0), - HI6421V600_LDO(LDO34, ldo34_voltages, + HI6421V600_LDO(ldo34, ldo34_voltages, 0x33, 0x01, 0x6e, 20000, 120, 0, 0), From 5e36129f2b4e9629513670fc1df97545ab4bd5a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Jul 2021 12:22:42 +0200 Subject: [PATCH 067/177] regulator: hi6421v600: rename voltage range arrays The arrays containing the regulator's voltage ranges are currently named after the first ldo which uses such range. However, it sounds a lot clearer if those are named with the voltage range instead. No functional changes. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/1bdff1d1f23753b69c8044160decfad1e8553d08.1627121912.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/hi6421v600-regulator.c | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/regulator/hi6421v600-regulator.c b/drivers/regulator/hi6421v600-regulator.c index d85f51cf2ba3..662d87ae61cb 100644 --- a/drivers/regulator/hi6421v600-regulator.c +++ b/drivers/regulator/hi6421v600-regulator.c @@ -27,34 +27,34 @@ struct hi6421_spmi_reg_info { u32 eco_uA; }; -static const unsigned int ldo3_voltages[] = { +static const unsigned int range_1v5_to_2v0[] = { 1500000, 1550000, 1600000, 1650000, 1700000, 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, 1900000, 1925000, 1950000, 2000000 }; -static const unsigned int ldo4_voltages[] = { +static const unsigned int range_1v725_to_1v9[] = { 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, 1900000 }; -static const unsigned int ldo9_voltages[] = { +static const unsigned int range_1v75_to_3v3[] = { 1750000, 1800000, 1825000, 2800000, 2850000, 2950000, 3000000, 3300000 }; -static const unsigned int ldo15_voltages[] = { +static const unsigned int range_1v8_to_3v0[] = { 1800000, 1850000, 2400000, 2600000, 2700000, 2850000, 2950000, 3000000 }; -static const unsigned int ldo17_voltages[] = { +static const unsigned int range_2v5_to_3v3[] = { 2500000, 2600000, 2700000, 2800000, 3000000, 3100000, 3200000, 3300000 }; -static const unsigned int ldo34_voltages[] = { +static const unsigned int range_2v6_to_3v3[] = { 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000 }; @@ -196,35 +196,35 @@ enum hi6421_spmi_regulator_id { }; static struct hi6421_spmi_reg_info regulator_info[] = { - HI6421V600_LDO(ldo3, ldo3_voltages, + HI6421V600_LDO(ldo3, range_1v5_to_2v0, 0x16, 0x01, 0x51, 20000, 120, 0, 0), - HI6421V600_LDO(ldo4, ldo4_voltages, + HI6421V600_LDO(ldo4, range_1v725_to_1v9, 0x17, 0x01, 0x52, 20000, 120, 0x10, 10000), - HI6421V600_LDO(ldo9, ldo9_voltages, + HI6421V600_LDO(ldo9, range_1v75_to_3v3, 0x1c, 0x01, 0x57, 20000, 360, 0x10, 10000), - HI6421V600_LDO(ldo15, ldo15_voltages, + HI6421V600_LDO(ldo15, range_1v8_to_3v0, 0x21, 0x01, 0x5c, 20000, 360, 0x10, 10000), - HI6421V600_LDO(ldo16, ldo15_voltages, + HI6421V600_LDO(ldo16, range_1v8_to_3v0, 0x22, 0x01, 0x5d, 20000, 360, 0x10, 10000), - HI6421V600_LDO(ldo17, ldo17_voltages, + HI6421V600_LDO(ldo17, range_2v5_to_3v3, 0x23, 0x01, 0x5e, 20000, 120, 0x10, 10000), - HI6421V600_LDO(ldo33, ldo17_voltages, + HI6421V600_LDO(ldo33, range_2v5_to_3v3, 0x32, 0x01, 0x6d, 20000, 120, 0, 0), - HI6421V600_LDO(ldo34, ldo34_voltages, + HI6421V600_LDO(ldo34, range_2v6_to_3v3, 0x33, 0x01, 0x6e, 20000, 120, 0, 0), From 9583db2332e3426129612e0ef69f70fbadb6053b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2021 15:33:38 +0200 Subject: [PATCH 068/177] ext2: make ext2_iomap_ops available unconditionally ext2_iomap_ops will be used for the FIEMAP support going forward, so make it available unconditionally. Link: https://lore.kernel.org/r/20210720133341.405438-2-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ext2/inode.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index dadb121beb22..3e9a04770f49 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -799,7 +799,6 @@ int ext2_get_block(struct inode *inode, sector_t iblock, } -#ifdef CONFIG_FS_DAX static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned flags, struct iomap *iomap, struct iomap *srcmap) { @@ -852,10 +851,6 @@ const struct iomap_ops ext2_iomap_ops = { .iomap_begin = ext2_iomap_begin, .iomap_end = ext2_iomap_end, }; -#else -/* Define empty ops for !CONFIG_FS_DAX case to avoid ugly ifdefs */ -const struct iomap_ops ext2_iomap_ops; -#endif /* CONFIG_FS_DAX */ int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) From 8b1e7076d26b935af7caec33dee2837c0ad7dbb5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2021 15:33:39 +0200 Subject: [PATCH 069/177] ext2: use iomap_fiemap to implement ->fiemap Switch from generic_block_fiemap to use the iomap version. The only interesting part is that ext2_get_blocks gets confused when being asked for overly long ranges, so copy over the limit to the inode size from generic_block_fiemap into ext2_fiemap. Link: https://lore.kernel.org/r/20210720133341.405438-3-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ext2/Kconfig | 1 + fs/ext2/inode.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/ext2/Kconfig b/fs/ext2/Kconfig index 54eec9185627..1248ff4ef562 100644 --- a/fs/ext2/Kconfig +++ b/fs/ext2/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config EXT2_FS tristate "Second extended fs support" + select FS_IOMAP help Ext2 is a standard Linux file system for hard disks. diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 3e9a04770f49..04f0def0f5eb 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -855,8 +855,14 @@ const struct iomap_ops ext2_iomap_ops = { int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { - return generic_block_fiemap(inode, fieinfo, start, len, - ext2_get_block); + int ret; + + inode_lock(inode); + len = min_t(u64, len, i_size_read(inode)); + ret = iomap_fiemap(inode, fieinfo, start, len, &ext2_iomap_ops); + inode_unlock(inode); + + return ret; } static int ext2_writepage(struct page *page, struct writeback_control *wbc) From e0cba89d22b7041202c33e4d1ae4d2006d7e0190 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2021 15:33:40 +0200 Subject: [PATCH 070/177] hpfs: use iomap_fiemap to implement ->fiemap hpfs is the last user of generic_block_fiemap, so add a trivial iomap_ops based on the ext2 version and switch to iomap_fiemap. Link: https://lore.kernel.org/r/20210720133341.405438-4-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/hpfs/Kconfig | 1 + fs/hpfs/file.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/fs/hpfs/Kconfig b/fs/hpfs/Kconfig index 2b36dc6f0a10..ec975f466877 100644 --- a/fs/hpfs/Kconfig +++ b/fs/hpfs/Kconfig @@ -2,6 +2,7 @@ config HPFS_FS tristate "OS/2 HPFS file system support" depends on BLOCK + select FS_IOMAP help OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS is the file system used for organizing files on OS/2 hard disk diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index c3a49aacf20a..fb37f57130aa 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -9,6 +9,7 @@ #include "hpfs_fn.h" #include +#include #include #define BLOCKS(size) (((size) + 511) >> 9) @@ -116,6 +117,47 @@ static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_he return r; } +static int hpfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned flags, struct iomap *iomap, struct iomap *srcmap) +{ + struct super_block *sb = inode->i_sb; + unsigned int blkbits = inode->i_blkbits; + unsigned int n_secs; + secno s; + + if (WARN_ON_ONCE(flags & (IOMAP_WRITE | IOMAP_ZERO))) + return -EINVAL; + + iomap->bdev = inode->i_sb->s_bdev; + iomap->offset = offset; + + hpfs_lock(sb); + s = hpfs_bmap(inode, offset >> blkbits, &n_secs); + if (s) { + n_secs = hpfs_search_hotfix_map_for_range(sb, s, + min_t(loff_t, n_secs, length)); + if (unlikely(!n_secs)) { + s = hpfs_search_hotfix_map(sb, s); + n_secs = 1; + } + iomap->type = IOMAP_MAPPED; + iomap->flags = IOMAP_F_MERGED; + iomap->addr = (u64)s << blkbits; + iomap->length = (u64)n_secs << blkbits; + } else { + iomap->type = IOMAP_HOLE; + iomap->addr = IOMAP_NULL_ADDR; + iomap->length = 1 << blkbits; + } + + hpfs_unlock(sb); + return 0; +} + +static const struct iomap_ops hpfs_iomap_ops = { + .iomap_begin = hpfs_iomap_begin, +}; + static int hpfs_readpage(struct file *file, struct page *page) { return mpage_readpage(page, hpfs_get_block); @@ -192,7 +234,14 @@ static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block) static int hpfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { - return generic_block_fiemap(inode, fieinfo, start, len, hpfs_get_block); + int ret; + + inode_lock(inode); + len = min_t(u64, len, i_size_read(inode)); + ret = iomap_fiemap(inode, fieinfo, start, len, &hpfs_iomap_ops); + inode_unlock(inode); + + return ret; } const struct address_space_operations hpfs_aops = { From 9acb9c48b9408bbb6ade90e3f3192ee38e2589b3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2021 15:33:41 +0200 Subject: [PATCH 071/177] fs: remove generic_block_fiemap Remove the now unused generic_block_fiemap helper. Link: https://lore.kernel.org/r/20210720133341.405438-5-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ioctl.c | 203 ----------------------------------------- include/linux/fiemap.h | 4 - 2 files changed, 207 deletions(-) diff --git a/fs/ioctl.c b/fs/ioctl.c index 1e2204fa9963..eea8267ae1f2 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -263,209 +263,6 @@ static long ioctl_file_clone_range(struct file *file, args.src_length, args.dest_offset); } -#ifdef CONFIG_BLOCK - -static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) -{ - return (offset >> inode->i_blkbits); -} - -static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) -{ - return (blk << inode->i_blkbits); -} - -/** - * __generic_block_fiemap - FIEMAP for block based inodes (no locking) - * @inode: the inode to map - * @fieinfo: the fiemap info struct that will be passed back to userspace - * @start: where to start mapping in the inode - * @len: how much space to map - * @get_block: the fs's get_block function - * - * This does FIEMAP for block based inodes. Basically it will just loop - * through get_block until we hit the number of extents we want to map, or we - * go past the end of the file and hit a hole. - * - * If it is possible to have data blocks beyond a hole past @inode->i_size, then - * please do not use this function, it will stop at the first unmapped block - * beyond i_size. - * - * If you use this function directly, you need to do your own locking. Use - * generic_block_fiemap if you want the locking done for you. - */ -static int __generic_block_fiemap(struct inode *inode, - struct fiemap_extent_info *fieinfo, loff_t start, - loff_t len, get_block_t *get_block) -{ - struct buffer_head map_bh; - sector_t start_blk, last_blk; - loff_t isize = i_size_read(inode); - u64 logical = 0, phys = 0, size = 0; - u32 flags = FIEMAP_EXTENT_MERGED; - bool past_eof = false, whole_file = false; - int ret = 0; - - ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC); - if (ret) - return ret; - - /* - * Either the i_mutex or other appropriate locking needs to be held - * since we expect isize to not change at all through the duration of - * this call. - */ - if (len >= isize) { - whole_file = true; - len = isize; - } - - /* - * Some filesystems can't deal with being asked to map less than - * blocksize, so make sure our len is at least block length. - */ - if (logical_to_blk(inode, len) == 0) - len = blk_to_logical(inode, 1); - - start_blk = logical_to_blk(inode, start); - last_blk = logical_to_blk(inode, start + len - 1); - - do { - /* - * we set b_size to the total size we want so it will map as - * many contiguous blocks as possible at once - */ - memset(&map_bh, 0, sizeof(struct buffer_head)); - map_bh.b_size = len; - - ret = get_block(inode, start_blk, &map_bh, 0); - if (ret) - break; - - /* HOLE */ - if (!buffer_mapped(&map_bh)) { - start_blk++; - - /* - * We want to handle the case where there is an - * allocated block at the front of the file, and then - * nothing but holes up to the end of the file properly, - * to make sure that extent at the front gets properly - * marked with FIEMAP_EXTENT_LAST - */ - if (!past_eof && - blk_to_logical(inode, start_blk) >= isize) - past_eof = 1; - - /* - * First hole after going past the EOF, this is our - * last extent - */ - if (past_eof && size) { - flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST; - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, - flags); - } else if (size) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, flags); - size = 0; - } - - /* if we have holes up to/past EOF then we're done */ - if (start_blk > last_blk || past_eof || ret) - break; - } else { - /* - * We have gone over the length of what we wanted to - * map, and it wasn't the entire file, so add the extent - * we got last time and exit. - * - * This is for the case where say we want to map all the - * way up to the second to the last block in a file, but - * the last block is a hole, making the second to last - * block FIEMAP_EXTENT_LAST. In this case we want to - * see if there is a hole after the second to last block - * so we can mark it properly. If we found data after - * we exceeded the length we were requesting, then we - * are good to go, just add the extent to the fieinfo - * and break - */ - if (start_blk > last_blk && !whole_file) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, - flags); - break; - } - - /* - * if size != 0 then we know we already have an extent - * to add, so add it. - */ - if (size) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, - flags); - if (ret) - break; - } - - logical = blk_to_logical(inode, start_blk); - phys = blk_to_logical(inode, map_bh.b_blocknr); - size = map_bh.b_size; - flags = FIEMAP_EXTENT_MERGED; - - start_blk += logical_to_blk(inode, size); - - /* - * If we are past the EOF, then we need to make sure as - * soon as we find a hole that the last extent we found - * is marked with FIEMAP_EXTENT_LAST - */ - if (!past_eof && logical + size >= isize) - past_eof = true; - } - cond_resched(); - if (fatal_signal_pending(current)) { - ret = -EINTR; - break; - } - - } while (1); - - /* If ret is 1 then we just hit the end of the extent array */ - if (ret == 1) - ret = 0; - - return ret; -} - -/** - * generic_block_fiemap - FIEMAP for block based inodes - * @inode: The inode to map - * @fieinfo: The mapping information - * @start: The initial block to map - * @len: The length of the extect to attempt to map - * @get_block: The block mapping function for the fs - * - * Calls __generic_block_fiemap to map the inode, after taking - * the inode's mutex lock. - */ - -int generic_block_fiemap(struct inode *inode, - struct fiemap_extent_info *fieinfo, u64 start, - u64 len, get_block_t *get_block) -{ - int ret; - inode_lock(inode); - ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block); - inode_unlock(inode); - return ret; -} -EXPORT_SYMBOL(generic_block_fiemap); - -#endif /* CONFIG_BLOCK */ - /* * This provides compatibility with legacy XFS pre-allocation ioctls * which predate the fallocate syscall. diff --git a/include/linux/fiemap.h b/include/linux/fiemap.h index 4e624c466583..c50882f19235 100644 --- a/include/linux/fiemap.h +++ b/include/linux/fiemap.h @@ -18,8 +18,4 @@ int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo, int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical, u64 phys, u64 len, u32 flags); -int generic_block_fiemap(struct inode *inode, - struct fiemap_extent_info *fieinfo, u64 start, u64 len, - get_block_t *get_block); - #endif /* _LINUX_FIEMAP_H 1 */ From 7882c55ef64a8179160f24d86e82e525ffcce020 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 27 Jul 2021 16:22:12 -0700 Subject: [PATCH 072/177] filesystems/locking: fix Malformed table warning Update the bottom border to be the same as the top border. Documentation/filesystems/locking.rst:274: WARNING: Malformed table. Bottom/header table border does not match top border. Fixes: 730633f0b7f9 ("mm: Protect operations adding pages to page cache with invalidate_lock") Link: https://lore.kernel.org/r/20210727232212.12510-1-rdunlap@infradead.org Signed-off-by: Randy Dunlap Reviewed-by: Darrick J. Wong Cc: Darrick J. Wong Cc: Christoph Hellwig Cc: Jan Kara Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Jan Kara --- Documentation/filesystems/locking.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index 38a3097b6f1c..2a75dd5da7b5 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -295,7 +295,7 @@ is_partially_uptodate: yes error_remove_page: yes swap_activate: no swap_deactivate: no -====================== ======================== ========= +====================== ======================== ========= =============== ->write_begin(), ->write_end() and ->readpage() may be called from the request handler (/dev/loop). From 15b4d2b972014b789f22d9267bcff1cc48153738 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Fri, 30 Jul 2021 20:32:06 +0800 Subject: [PATCH 073/177] regulator: rtq2134: Fix coding style Add empty space and put constant number to the right side for 'if' judgement. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1627648326-5026-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/rtq2134-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/rtq2134-regulator.c b/drivers/regulator/rtq2134-regulator.c index e09419cc2793..f21e3f8b21f2 100644 --- a/drivers/regulator/rtq2134-regulator.c +++ b/drivers/regulator/rtq2134-regulator.c @@ -311,7 +311,7 @@ static const struct rtq2134_regulator_desc rtq2134_regulator_descs[] = { static bool rtq2134_is_accissible_reg(struct device *dev, unsigned int reg) { - if (RTQ2134_REG_IO_CHIPNAME <= reg && reg <= RTQ2134_REG_BUCK3_SLEWCTRL) + if (reg >= RTQ2134_REG_IO_CHIPNAME && reg <= RTQ2134_REG_BUCK3_SLEWCTRL) return true; return false; } @@ -329,7 +329,7 @@ static int rtq2134_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regulator_dev *rdev; - struct regulator_config regulator_cfg= {}; + struct regulator_config regulator_cfg = {}; int i; regmap = devm_regmap_init_i2c(i2c, &rtq2134_regmap_config); From 29c34975c9391d3ad1fd5dd3c92ba0d41afe9549 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 2 Aug 2021 14:37:41 +0800 Subject: [PATCH 074/177] regmap: allow const array for {devm_,}regmap_field_bulk_alloc reg_fields The reg_fields array fed to {devm_}regmap_field_bulk_alloc is currently not const, which is not correct on semantics (the functions shouldn't change reg_field contents) and prevents pre-defined const reg_field array to be used. As the implementation of this function doesn't change the content of it, just add const to its prototype. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20210802063741.76301-1-icenowy@sipeed.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- include/linux/regmap.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index dcfa99ea7f31..6ad41d0720ba 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1298,7 +1298,7 @@ EXPORT_SYMBOL_GPL(devm_regmap_field_alloc); */ int regmap_field_bulk_alloc(struct regmap *regmap, struct regmap_field **rm_field, - struct reg_field *reg_field, + const struct reg_field *reg_field, int num_fields) { struct regmap_field *rf; @@ -1334,7 +1334,7 @@ EXPORT_SYMBOL_GPL(regmap_field_bulk_alloc); int devm_regmap_field_bulk_alloc(struct device *dev, struct regmap *regmap, struct regmap_field **rm_field, - struct reg_field *reg_field, + const struct reg_field *reg_field, int num_fields) { struct regmap_field *rf; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f5f08dd0a116..77755196277c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1269,12 +1269,13 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field); int regmap_field_bulk_alloc(struct regmap *regmap, struct regmap_field **rm_field, - struct reg_field *reg_field, + const struct reg_field *reg_field, int num_fields); void regmap_field_bulk_free(struct regmap_field *field); int devm_regmap_field_bulk_alloc(struct device *dev, struct regmap *regmap, struct regmap_field **field, - struct reg_field *reg_field, int num_fields); + const struct reg_field *reg_field, + int num_fields); void devm_regmap_field_bulk_free(struct device *dev, struct regmap_field *field); From 6e95b23a5b2d1fcbe5a84a362170a4871a3d5731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 27 Jul 2021 14:42:26 +0200 Subject: [PATCH 075/177] spi: imx: Implement support for CS_WORD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This only works when the native chipselect is in use. On a board with a Ti ADS7950 8 channel ADC. This patch reduces the time to read out all channels once from 280 us to 20 us. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210727124226.5571-1-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 593b63be73de..340fd1245bc2 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1176,6 +1176,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, * dynamic_burst in that case. */ if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode && + !(spi->mode & SPI_CS_WORD) && (spi_imx->bits_per_word == 8 || spi_imx->bits_per_word == 16 || spi_imx->bits_per_word == 32)) { @@ -1611,6 +1612,15 @@ static int spi_imx_probe(struct platform_device *pdev) is_imx53_ecspi(spi_imx)) spi_imx->bitbang.master->mode_bits |= SPI_LOOP | SPI_READY; + if (is_imx51_ecspi(spi_imx) && + device_property_read_u32(&pdev->dev, "cs-gpios", NULL)) + /* + * When using HW-CS implementing SPI_CS_WORD can be done by just + * setting the burst length to the word size. This is + * considerably faster than manually controlling the CS. + */ + spi_imx->bitbang.master->mode_bits |= SPI_CS_WORD; + spi_imx->spi_drctl = spi_drctl; init_completion(&spi_imx->xfer_done); From 784ed36958390c511cd48db061401f9ba20ba67a Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 3 Aug 2021 16:50:43 +0800 Subject: [PATCH 076/177] regulator: sy8824x: Enable REGCACHE_FLAT Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Since the register map is small -- there's only one register in sy8824c and sy8824e, there are only two registers in sy20276 and sy20278, so we use a FLAT regmap cache. Signed-off-by: Jisheng Zhang Link: https://lore.kernel.org/r/20210803165043.042ec24d@xhacker.debian Signed-off-by: Mark Brown --- drivers/regulator/sy8824x.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/sy8824x.c b/drivers/regulator/sy8824x.c index 62d243f3b904..5e915cf307b3 100644 --- a/drivers/regulator/sy8824x.c +++ b/drivers/regulator/sy8824x.c @@ -25,6 +25,7 @@ struct sy8824_config { unsigned int vsel_min; unsigned int vsel_step; unsigned int vsel_count; + const struct regmap_config *config; }; struct sy8824_device_info { @@ -110,6 +111,15 @@ static int sy8824_regulator_register(struct sy8824_device_info *di, static const struct regmap_config sy8824_regmap_config = { .reg_bits = 8, .val_bits = 8, + .num_reg_defaults_raw = 1, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config sy20276_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .num_reg_defaults_raw = 2, + .cache_type = REGCACHE_FLAT, }; static int sy8824_i2c_probe(struct i2c_client *client) @@ -134,7 +144,7 @@ static int sy8824_i2c_probe(struct i2c_client *client) di->dev = dev; di->cfg = of_device_get_match_data(dev); - regmap = devm_regmap_init_i2c(client, &sy8824_regmap_config); + regmap = devm_regmap_init_i2c(client, di->cfg->config); if (IS_ERR(regmap)) { dev_err(dev, "Failed to allocate regmap!\n"); return PTR_ERR(regmap); @@ -160,6 +170,7 @@ static const struct sy8824_config sy8824c_cfg = { .vsel_min = 762500, .vsel_step = 12500, .vsel_count = 64, + .config = &sy8824_regmap_config, }; static const struct sy8824_config sy8824e_cfg = { @@ -169,6 +180,7 @@ static const struct sy8824_config sy8824e_cfg = { .vsel_min = 700000, .vsel_step = 12500, .vsel_count = 64, + .config = &sy8824_regmap_config, }; static const struct sy8824_config sy20276_cfg = { @@ -178,6 +190,7 @@ static const struct sy8824_config sy20276_cfg = { .vsel_min = 600000, .vsel_step = 10000, .vsel_count = 128, + .config = &sy20276_regmap_config, }; static const struct sy8824_config sy20278_cfg = { @@ -187,6 +200,7 @@ static const struct sy8824_config sy20278_cfg = { .vsel_min = 762500, .vsel_step = 12500, .vsel_count = 64, + .config = &sy20276_regmap_config, }; static const struct of_device_id sy8824_dt_ids[] = { From 6bdd1c672a2afbcd1a6d50dc2351ef4ea0bbfc61 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 3 Aug 2021 16:52:11 +0800 Subject: [PATCH 077/177] regulator: sy8827n: Enable REGCACHE_FLAT Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Since the register map is small, we use a FLAT regmap cache. Signed-off-by: Jisheng Zhang Link: https://lore.kernel.org/r/20210803165211.3b00db29@xhacker.debian Signed-off-by: Mark Brown --- drivers/regulator/sy8827n.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/regulator/sy8827n.c b/drivers/regulator/sy8827n.c index 52e8c17afe24..7d5d9f879ce3 100644 --- a/drivers/regulator/sy8827n.c +++ b/drivers/regulator/sy8827n.c @@ -19,6 +19,10 @@ #define SY8827N_MODE (1 << 6) #define SY8827N_VSEL1 1 #define SY8827N_CTRL 2 +#define SY8827N_ID1 3 +#define SY8827N_ID2 4 +#define SY8827N_PGOOD 5 +#define SY8827N_MAX (SY8827N_PGOOD + 1) #define SY8827N_NVOLTAGES 64 #define SY8827N_VSELMIN 600000 @@ -102,9 +106,19 @@ static int sy8827n_regulator_register(struct sy8827n_device_info *di, return PTR_ERR_OR_ZERO(rdev); } +static bool sy8827n_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == SY8827N_PGOOD) + return true; + return false; +} + static const struct regmap_config sy8827n_regmap_config = { .reg_bits = 8, .val_bits = 8, + .volatile_reg = sy8827n_volatile_reg, + .num_reg_defaults_raw = SY8827N_MAX, + .cache_type = REGCACHE_FLAT, }; static int sy8827n_i2c_probe(struct i2c_client *client) From e5dad32d90e0e9b006f5c330e92fa0ec9042ae79 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 3 Aug 2021 18:44:52 +1000 Subject: [PATCH 078/177] regulator: sy7636a: Remove the poll_enable_time From testing on hardware the poll_enable_time isn't required and sometimes causes the driver probe to fail so let's remove it. Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20210803084456.198-5-alistair@alistair23.me Signed-off-by: Mark Brown --- drivers/regulator/sy7636a-regulator.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index e021ae08cbaa..c3b34cac8c7f 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -13,8 +13,6 @@ #include #include -#define SY7636A_POLL_ENABLED_TIME 500 - static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev) { int ret; @@ -61,7 +59,6 @@ static const struct regulator_desc desc = { .owner = THIS_MODULE, .enable_reg = SY7636A_REG_OPERATION_MODE_CRL, .enable_mask = SY7636A_OPERATION_MODE_CRL_ONOFF, - .poll_enabled_time = SY7636A_POLL_ENABLED_TIME, .regulators_node = of_match_ptr("regulators"), .of_match = of_match_ptr("vcom"), }; From 4cafe1aeb5fb4eb1778d5e1b91d50a078369dbe1 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 3 Aug 2021 18:44:53 +1000 Subject: [PATCH 079/177] regulator: sy7636a: Use the parent driver data Use the parent's MFD data instead of our data. Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20210803084456.198-6-alistair@alistair23.me Signed-off-by: Mark Brown --- drivers/regulator/sy7636a-regulator.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index c3b34cac8c7f..0bd21c3ea24a 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -33,7 +33,7 @@ static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev) static int sy7636a_get_status(struct regulator_dev *rdev) { - struct sy7636a *sy7636a = rdev_get_drvdata(rdev); + struct sy7636a *sy7636a = dev_get_drvdata(rdev->dev.parent); int ret = 0; ret = gpiod_get_value_cansleep(sy7636a->pgood_gpio); @@ -76,9 +76,9 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sy7636a); - gdp = devm_gpiod_get(sy7636a->dev, "epd-pwr-good", GPIOD_IN); + gdp = devm_gpiod_get(pdev->dev.parent, "epd-pwr-good", GPIOD_IN); if (IS_ERR(gdp)) { - dev_err(sy7636a->dev, "Power good GPIO fault %ld\n", PTR_ERR(gdp)); + dev_err(pdev->dev.parent, "Power good GPIO fault %ld\n", PTR_ERR(gdp)); return PTR_ERR(gdp); } @@ -86,18 +86,18 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) ret = regmap_write(sy7636a->regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0); if (ret) { - dev_err(sy7636a->dev, "Failed to initialize regulator: %d\n", ret); + dev_err(pdev->dev.parent, "Failed to initialize regulator: %d\n", ret); return ret; } config.dev = &pdev->dev; - config.dev->of_node = sy7636a->dev->of_node; + config.dev->of_node = pdev->dev.parent->of_node; config.driver_data = sy7636a; config.regmap = sy7636a->regmap; rdev = devm_regulator_register(&pdev->dev, &desc, &config); if (IS_ERR(rdev)) { - dev_err(sy7636a->dev, "Failed to register %s regulator\n", + dev_err(pdev->dev.parent, "Failed to register %s regulator\n", pdev->name); return PTR_ERR(rdev); } From d38d49b140043bba3ea27b89cca5fefaf08e2034 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 3 Aug 2021 18:44:54 +1000 Subject: [PATCH 080/177] regulator: sy7636a: Store the epd-pwr-good GPIO locally Instead of storing the GPIO state in the mfd (where it isn't used) store it in the regulator. Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20210803084456.198-7-alistair@alistair23.me Signed-off-by: Mark Brown --- drivers/regulator/sy7636a-regulator.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index 0bd21c3ea24a..37bf2a3c06b7 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -13,6 +13,11 @@ #include #include +struct sy7636a_data { + struct sy7636a *sy7636a; + struct gpio_desc *pgood_gpio; +}; + static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev) { int ret; @@ -33,10 +38,10 @@ static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev) static int sy7636a_get_status(struct regulator_dev *rdev) { - struct sy7636a *sy7636a = dev_get_drvdata(rdev->dev.parent); + struct sy7636a_data *data = dev_get_drvdata(rdev->dev.parent); int ret = 0; - ret = gpiod_get_value_cansleep(sy7636a->pgood_gpio); + ret = gpiod_get_value_cansleep(data->pgood_gpio); if (ret < 0) dev_err(&rdev->dev, "Failed to read pgood gpio: %d\n", ret); @@ -69,20 +74,26 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) struct regulator_config config = { }; struct regulator_dev *rdev; struct gpio_desc *gdp; + struct sy7636a_data *data; int ret; if (!sy7636a) return -EPROBE_DEFER; - platform_set_drvdata(pdev, sy7636a); - gdp = devm_gpiod_get(pdev->dev.parent, "epd-pwr-good", GPIOD_IN); if (IS_ERR(gdp)) { dev_err(pdev->dev.parent, "Power good GPIO fault %ld\n", PTR_ERR(gdp)); return PTR_ERR(gdp); } - sy7636a->pgood_gpio = gdp; + data = devm_kzalloc(&pdev->dev, sizeof(struct sy7636a_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->sy7636a = sy7636a; + data->pgood_gpio = gdp; + + platform_set_drvdata(pdev, data); ret = regmap_write(sy7636a->regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0); if (ret) { From 7c72dc56a631b87043e3c5838f5094db30d8c58d Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 26 Jul 2021 16:59:50 +0300 Subject: [PATCH 081/177] spi: spi-ep93xx: Prepare clock before using it Use clk_prepare_enable()/clk_disable_unprepare() in preparation for switch to Common Clock Framework, otherwise the following is visible: WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:1011 clk_core_enable+0x9c/0xbc Enabling unprepared ep93xx-spi.0 ... Hardware name: Cirrus Logic EDB9302 Evaluation Board ... clk_core_enable clk_core_enable_lock ep93xx_spi_prepare_hardware __spi_pump_messages __spi_sync spi_sync spi_sync_transfer.constprop.0 regmap_spi_write _regmap_raw_write_impl _regmap_bus_raw_write _regmap_update_bits regmap_update_bits_base cs4271_component_probe snd_soc_component_probe soc_probe_component snd_soc_bind_card edb93xx_probe ... spi_master spi0: failed to prepare transfer hardware: -108 Signed-off-by: Alexander Sverdlin Signed-off-by: Nikita Shubin Acked-by: Mark Brown Link: https://lore.kernel.org/r/20210726140001.24820-3-nikita.shubin@maquefel.me Signed-off-by: Mark Brown --- drivers/spi/spi-ep93xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index aa676559d273..5896a7b2fade 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -550,7 +550,7 @@ static int ep93xx_spi_prepare_hardware(struct spi_master *master) u32 val; int ret; - ret = clk_enable(espi->clk); + ret = clk_prepare_enable(espi->clk); if (ret) return ret; @@ -570,7 +570,7 @@ static int ep93xx_spi_unprepare_hardware(struct spi_master *master) val &= ~SSPCR1_SSE; writel(val, espi->mmio + SSPCR1); - clk_disable(espi->clk); + clk_disable_unprepare(espi->clk); return 0; } From b09bff2676be3ae286e6161a1a581a40c53a3c62 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 31 Jul 2021 21:33:42 +0800 Subject: [PATCH 082/177] spi: bcm2835aux: use 'unsigned int' instead of 'unsigned' Prefer 'unsigned int' to bare use of 'unsigned'. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20210731133342.432575-1-wangborong@cdjrlc.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835aux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index 37eab100a7d8..7d709a8c833b 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -143,12 +143,12 @@ static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) } #endif /* CONFIG_DEBUG_FS */ -static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg) +static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned int reg) { return readl(bs->regs + reg); } -static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg, +static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned int reg, u32 val) { writel(val, bs->regs + reg); From 8c33ebfeeb597ea953df93f84ea25482d29c664f Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Wed, 4 Aug 2021 21:37:17 +0800 Subject: [PATCH 083/177] spi: move cs spi_delay to spi_device As we know, spi core layer has removed spi_set_cs_timing() API. So this patch moved spi_delay for cs_timing from spi_controller to spi_device, because cs timing should be set by spi_device but not controller. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210804133716.32040-1-Mason.Zhang@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 +++--- include/linux/spi/spi.h | 20 +++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c99181165321..5d68e6cd2a18 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -842,9 +842,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || !spi->controller->set_cs_timing) { if (activate) - spi_delay_exec(&spi->controller->cs_setup, NULL); + spi_delay_exec(&spi->cs_setup, NULL); else - spi_delay_exec(&spi->controller->cs_hold, NULL); + spi_delay_exec(&spi->cs_hold, NULL); } if (spi->mode & SPI_CS_HIGH) @@ -887,7 +887,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || !spi->controller->set_cs_timing) { if (!activate) - spi_delay_exec(&spi->controller->cs_inactive, NULL); + spi_delay_exec(&spi->cs_inactive, NULL); } } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 3a81b5d1c3cb..1efe2e08957e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -147,7 +147,11 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer); * not using a GPIO line) * @word_delay: delay to be inserted between consecutive * words of a transfer - * + * @cs_setup: delay to be introduced by the controller after CS is asserted + * @cs_hold: delay to be introduced by the controller before CS is deasserted + * @cs_inactive: delay to be introduced by the controller after CS is + * deasserted. If @cs_change_delay is used from @spi_transfer, then the + * two delays will be added up. * @statistics: statistics for the spi_device * * A @spi_device is used to interchange data between an SPI slave @@ -188,6 +192,10 @@ struct spi_device { int cs_gpio; /* LEGACY: chip select gpio */ struct gpio_desc *cs_gpiod; /* chip select gpio desc */ struct spi_delay word_delay; /* inter-word delay */ + /* CS delays */ + struct spi_delay cs_setup; + struct spi_delay cs_hold; + struct spi_delay cs_inactive; /* the statistics */ struct spi_statistics statistics; @@ -413,11 +421,6 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * controller has native support for memory like operations. * @unprepare_message: undo any work done by prepare_message(). * @slave_abort: abort the ongoing transfer request on an SPI slave controller - * @cs_setup: delay to be introduced by the controller after CS is asserted - * @cs_hold: delay to be introduced by the controller before CS is deasserted - * @cs_inactive: delay to be introduced by the controller after CS is - * deasserted. If @cs_change_delay is used from @spi_transfer, then the - * two delays will be added up. * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per * CS number. Any individual value may be -ENOENT for CS lines that * are not GPIOs (driven by the SPI controller itself). Use the cs_gpiods @@ -639,11 +642,6 @@ struct spi_controller { /* Optimized handlers for SPI memory-like operations. */ const struct spi_controller_mem_ops *mem_ops; - /* CS delays */ - struct spi_delay cs_setup; - struct spi_delay cs_hold; - struct spi_delay cs_inactive; - /* gpio chip select */ int *cs_gpios; struct gpio_desc **cs_gpiods; From 04e6bb0d6bb127bac929fb35edd2dd01613c9520 Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Wed, 4 Aug 2021 21:37:47 +0800 Subject: [PATCH 084/177] spi: modify set_cs_timing parameter This patch modified set_cs_timing parameter, no need pass in spi_delay to set_cs_timing callback. By the way, we modified the mediatek and tegra114 spi driver to fix build err. In mediatek spi driver, We have support set absolute time not clk_count, and call this function in prepare_message not user's API. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210804133746.6742-1-Mason.Zhang@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 107 +++++++++++++++++++++---------------- drivers/spi/spi-tegra114.c | 8 +-- include/linux/spi/spi.h | 3 +- 3 files changed, 66 insertions(+), 52 deletions(-) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index b34fbc913fd6..20569c4c1fb6 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -208,6 +208,65 @@ static void mtk_spi_reset(struct mtk_spi *mdata) writel(reg_val, mdata->base + SPI_CMD_REG); } +static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) +{ + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + struct spi_delay *cs_setup = &spi->cs_setup; + struct spi_delay *cs_hold = &spi->cs_hold; + struct spi_delay *cs_inactive = &spi->cs_inactive; + u16 setup, hold, inactive; + u32 reg_val; + int delay; + + delay = spi_delay_to_ns(cs_setup, NULL); + if (delay < 0) + return delay; + setup = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; + + delay = spi_delay_to_ns(cs_hold, NULL); + if (delay < 0) + return delay; + hold = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; + + delay = spi_delay_to_ns(cs_inactive, NULL); + if (delay < 0) + return delay; + inactive = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; + + setup = setup ? setup : 1; + hold = hold ? hold : 1; + inactive = inactive ? inactive : 1; + + reg_val = readl(mdata->base + SPI_CFG0_REG); + if (mdata->dev_comp->enhance_timing) { + hold = min(hold, 0xffff); + setup = min(setup, 0xffff); + reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); + reg_val |= (((hold - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); + reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + reg_val |= (((setup - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + } else { + hold = min(hold, 0xff); + setup = min(setup, 0xff); + reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET); + reg_val |= (((hold - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); + reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET); + reg_val |= (((setup - 1) & 0xff) + << SPI_CFG0_CS_SETUP_OFFSET); + } + writel(reg_val, mdata->base + SPI_CFG0_REG); + + inactive = min(inactive, 0xff); + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_CS_IDLE_MASK; + reg_val |= (((inactive - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + + return 0; +} + static int mtk_spi_prepare_message(struct spi_master *master, struct spi_message *msg) { @@ -284,6 +343,8 @@ static int mtk_spi_prepare_message(struct spi_master *master, << SPI_CFG1_GET_TICK_DLY_OFFSET); writel(reg_val, mdata->base + SPI_CFG1_REG); + /* set hw cs timing */ + mtk_spi_set_hw_cs_timing(spi); return 0; } @@ -528,52 +589,6 @@ static bool mtk_spi_can_dma(struct spi_master *master, (unsigned long)xfer->rx_buf % 4 == 0); } -static int mtk_spi_set_hw_cs_timing(struct spi_device *spi, - struct spi_delay *setup, - struct spi_delay *hold, - struct spi_delay *inactive) -{ - struct mtk_spi *mdata = spi_master_get_devdata(spi->master); - u16 setup_dly, hold_dly, inactive_dly; - u32 reg_val; - - if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) || - (hold && hold->unit != SPI_DELAY_UNIT_SCK) || - (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) { - dev_err(&spi->dev, - "Invalid delay unit, should be SPI_DELAY_UNIT_SCK\n"); - return -EINVAL; - } - - setup_dly = setup ? setup->value : 1; - hold_dly = hold ? hold->value : 1; - inactive_dly = inactive ? inactive->value : 1; - - reg_val = readl(mdata->base + SPI_CFG0_REG); - if (mdata->dev_comp->enhance_timing) { - reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); - reg_val |= (((hold_dly - 1) & 0xffff) - << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); - reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); - reg_val |= (((setup_dly - 1) & 0xffff) - << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); - } else { - reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET); - reg_val |= (((hold_dly - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); - reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET); - reg_val |= (((setup_dly - 1) & 0xff) - << SPI_CFG0_CS_SETUP_OFFSET); - } - writel(reg_val, mdata->base + SPI_CFG0_REG); - - reg_val = readl(mdata->base + SPI_CFG1_REG); - reg_val &= ~SPI_CFG1_CS_IDLE_MASK; - reg_val |= (((inactive_dly - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); - writel(reg_val, mdata->base + SPI_CFG1_REG); - - return 0; -} - static int mtk_spi_setup(struct spi_device *spi) { struct mtk_spi *mdata = spi_master_get_devdata(spi->master); diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 5131141bbf0d..e9de1d958bbd 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -717,12 +717,12 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, dma_release_channel(dma_chan); } -static int tegra_spi_set_hw_cs_timing(struct spi_device *spi, - struct spi_delay *setup, - struct spi_delay *hold, - struct spi_delay *inactive) +static int tegra_spi_set_hw_cs_timing(struct spi_device *spi) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + struct spi_delay *setup = &spi->cs_setup; + struct spi_delay *hold = &spi->cs_hold; + struct spi_delay *inactive = &spi->cs_inactive; u8 setup_dly, hold_dly, inactive_dly; u32 setup_hold; u32 spi_cs_timing; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 1efe2e08957e..8371bca13729 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -554,8 +554,7 @@ struct spi_controller { * to configure specific CS timing through spi_set_cs_timing() after * spi_setup(). */ - int (*set_cs_timing)(struct spi_device *spi, struct spi_delay *setup, - struct spi_delay *hold, struct spi_delay *inactive); + int (*set_cs_timing)(struct spi_device *spi); /* bidirectional bulk transfers * From daaca3156dd9832f57709cb858f10ff6b22a8821 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Thu, 5 Aug 2021 20:52:33 +0800 Subject: [PATCH 085/177] power: supply: sc27xx: Delete superfluous error message In the function sc27xx_fgu_probe(), when get irq failed, platform_get_irq() logs an error message, so remove redundant message here. Signed-off-by: Tang Bin Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 1ae8374e1ceb..ae45069bd5e1 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1229,10 +1229,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq resource specified\n"); + if (irq < 0) return irq; - } ret = devm_request_threaded_irq(data->dev, irq, NULL, sc27xx_fgu_interrupt, From f9ac97307b620a08b071a4db33ddb4a26c5b8eb0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:30:56 +0200 Subject: [PATCH 086/177] power: supply: axp288_fuel_gauge: Fix define alignment The values of various defines used in the driver are not aligned properly when tabsize is set to 8 (I guess they were created with a different tabsize). Properly align the defines to make the code easier to read. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 2ba2d8d6b8e6..99928789040d 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -23,34 +23,34 @@ #include #include -#define PS_STAT_VBUS_TRIGGER (1 << 0) -#define PS_STAT_BAT_CHRG_DIR (1 << 2) -#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) -#define PS_STAT_VBUS_VALID (1 << 4) -#define PS_STAT_VBUS_PRESENT (1 << 5) +#define PS_STAT_VBUS_TRIGGER (1 << 0) +#define PS_STAT_BAT_CHRG_DIR (1 << 2) +#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) +#define PS_STAT_VBUS_VALID (1 << 4) +#define PS_STAT_VBUS_PRESENT (1 << 5) -#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) +#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) #define CHRG_STAT_BAT_VALID (1 << 4) -#define CHRG_STAT_BAT_PRESENT (1 << 5) +#define CHRG_STAT_BAT_PRESENT (1 << 5) #define CHRG_STAT_CHARGING (1 << 6) #define CHRG_STAT_PMIC_OTP (1 << 7) #define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ -#define CHRG_CCCV_CC_BIT_POS 0 +#define CHRG_CCCV_CC_BIT_POS 0 #define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ -#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ +#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ #define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */ #define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ -#define CHRG_CCCV_CV_BIT_POS 5 +#define CHRG_CCCV_CV_BIT_POS 5 #define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ #define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ #define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ #define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ #define CHRG_CCCV_CHG_EN (1 << 7) -#define FG_CNTL_OCV_ADJ_STAT (1 << 2) +#define FG_CNTL_OCV_ADJ_STAT (1 << 2) #define FG_CNTL_OCV_ADJ_EN (1 << 3) -#define FG_CNTL_CAP_ADJ_STAT (1 << 4) +#define FG_CNTL_CAP_ADJ_STAT (1 << 4) #define FG_CNTL_CAP_ADJ_EN (1 << 5) #define FG_CNTL_CC_EN (1 << 6) #define FG_CNTL_GAUGE_EN (1 << 7) @@ -71,23 +71,23 @@ #define FG_CC_CAP_VALID (1 << 7) #define FG_CC_CAP_VAL_MASK 0x7F -#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */ +#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */ #define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */ -#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */ +#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */ #define FG_LOW_CAP_WARN_THR 14 /* 14 perc */ #define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */ #define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */ -#define NR_RETRY_CNT 3 -#define DEV_NAME "axp288_fuel_gauge" +#define NR_RETRY_CNT 3 +#define DEV_NAME "axp288_fuel_gauge" /* 1.1mV per LSB expressed in uV */ #define VOLTAGE_FROM_ADC(a) ((a * 11) / 10) /* properties converted to uV, uA */ -#define PROP_VOLT(a) ((a) * 1000) -#define PROP_CURR(a) ((a) * 1000) +#define PROP_VOLT(a) ((a) * 1000) +#define PROP_CURR(a) ((a) * 1000) -#define AXP288_FG_INTR_NUM 6 +#define AXP288_FG_INTR_NUM 6 enum { QWBTU_IRQ = 0, WBTU_IRQ, From fc0db6556c4170205391cdcf0f2de0bc99d4ef4e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:30:57 +0200 Subject: [PATCH 087/177] power: supply: axp288_fuel_gauge: Remove debugfs support The debugfs code is simply just dumping a bunch of registers, the same information can also easily be gotten through the regmap debugfs interface or through the i2cdump utility. I've not used the debugfs interface once in all these years that I've been working on the axp288_fuel_gauge driver, so lets just remove it. Note this also removes the temperature-channels from the list of IIO ADC channels used by the driver, since these were only used in the debugfs interface. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 123 ----------------------- 1 file changed, 123 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 99928789040d..d189849564db 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #define PS_STAT_VBUS_TRIGGER (1 << 0) @@ -98,9 +96,6 @@ enum { }; enum { - BAT_TEMP = 0, - PMIC_TEMP, - SYSTEM_TEMP, BAT_CHRG_CURR, BAT_D_CURR, BAT_VOLT, @@ -204,119 +199,6 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); } -#ifdef CONFIG_DEBUG_FS -static int fuel_gauge_debug_show(struct seq_file *s, void *data) -{ - struct axp288_fg_info *info = s->private; - int raw_val, ret; - - seq_printf(s, " PWR_STATUS[%02x] : %02x\n", - AXP20X_PWR_INPUT_STATUS, - fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS)); - seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n", - AXP20X_PWR_OP_MODE, - fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE)); - seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n", - AXP20X_CHRG_CTRL1, - fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1)); - seq_printf(s, " VLTF[%02x] : %02x\n", - AXP20X_V_LTF_DISCHRG, - fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG)); - seq_printf(s, " VHTF[%02x] : %02x\n", - AXP20X_V_HTF_DISCHRG, - fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG)); - seq_printf(s, " CC_CTRL[%02x] : %02x\n", - AXP20X_CC_CTRL, - fuel_gauge_reg_readb(info, AXP20X_CC_CTRL)); - seq_printf(s, "BATTERY CAP[%02x] : %02x\n", - AXP20X_FG_RES, - fuel_gauge_reg_readb(info, AXP20X_FG_RES)); - seq_printf(s, " FG_RDC1[%02x] : %02x\n", - AXP288_FG_RDC1_REG, - fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG)); - seq_printf(s, " FG_RDC0[%02x] : %02x\n", - AXP288_FG_RDC0_REG, - fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG)); - seq_printf(s, " FG_OCV[%02x] : %04x\n", - AXP288_FG_OCVH_REG, - fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG)); - seq_printf(s, " FG_DES_CAP[%02x] : %04x\n", - AXP288_FG_DES_CAP1_REG, - fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG)); - seq_printf(s, " FG_CC_MTR[%02x] : %04x\n", - AXP288_FG_CC_MTR1_REG, - fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG)); - seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n", - AXP288_FG_OCV_CAP_REG, - fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG)); - seq_printf(s, " FG_CC_CAP[%02x] : %02x\n", - AXP288_FG_CC_CAP_REG, - fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG)); - seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n", - AXP288_FG_LOW_CAP_REG, - fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG)); - seq_printf(s, "TUNING_CTL0[%02x] : %02x\n", - AXP288_FG_TUNE0, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE0)); - seq_printf(s, "TUNING_CTL1[%02x] : %02x\n", - AXP288_FG_TUNE1, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE1)); - seq_printf(s, "TUNING_CTL2[%02x] : %02x\n", - AXP288_FG_TUNE2, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE2)); - seq_printf(s, "TUNING_CTL3[%02x] : %02x\n", - AXP288_FG_TUNE3, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE3)); - seq_printf(s, "TUNING_CTL4[%02x] : %02x\n", - AXP288_FG_TUNE4, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE4)); - seq_printf(s, "TUNING_CTL5[%02x] : %02x\n", - AXP288_FG_TUNE5, - fuel_gauge_reg_readb(info, AXP288_FG_TUNE5)); - - ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-batttemp : %d\n", raw_val); - ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-pmictemp : %d\n", raw_val); - ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-systtemp : %d\n", raw_val); - ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-chrgcurr : %d\n", raw_val); - ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-dchrgcur : %d\n", raw_val); - ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); - if (ret >= 0) - seq_printf(s, "axp288-battvolt : %d\n", raw_val); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug); - -static void fuel_gauge_create_debugfs(struct axp288_fg_info *info) -{ - info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL, - info, &fuel_gauge_debug_fops); -} - -static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) -{ - debugfs_remove(info->debug_file); -} -#else -static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info) -{ -} -static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) -{ -} -#endif - static void fuel_gauge_get_status(struct axp288_fg_info *info) { int pwr_stat, fg_res, curr, ret; @@ -753,9 +635,6 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; static const char * const iio_chan_name[] = { - [BAT_TEMP] = "axp288-batt-temp", - [PMIC_TEMP] = "axp288-pmic-temp", - [SYSTEM_TEMP] = "axp288-system-temp", [BAT_CHRG_CURR] = "axp288-chrg-curr", [BAT_D_CURR] = "axp288-chrg-d-curr", [BAT_VOLT] = "axp288-batt-volt", @@ -844,7 +723,6 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) goto out_free_iio_chan; } - fuel_gauge_create_debugfs(info); fuel_gauge_init_irq(info); return 0; @@ -869,7 +747,6 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev) int i; power_supply_unregister(info->bat); - fuel_gauge_remove_debugfs(info); for (i = 0; i < AXP288_FG_INTR_NUM; i++) if (info->irq[i] >= 0) From 8f6cc48e1aff3c1d641a65f3ad6d01c233269ea8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:30:58 +0200 Subject: [PATCH 088/177] power: supply: axp288_fuel_gauge: Silence the chatty IRQ mapping code Drop the IRQ mapping messages, because they are really not interesting at all. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index d189849564db..43cc171101f1 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -537,9 +537,6 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info) pirq, info->irq[i]); info->irq[i] = -1; goto intr_failed; - } else { - dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n", - pirq, info->irq[i]); } } return; From caa534c3ba40c6e8352b42cbbbca9ba481814ac8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:30:59 +0200 Subject: [PATCH 089/177] power: supply: axp288_fuel_gauge: Report register-address on readb / writeb errors When fuel_gauge_reg_readb()/_writeb() fails, report which register we were trying to read / write when the error happened. Also reword the message a bit: - Drop the axp288 prefix, dev_err() already prints this - Switch from telegram / abbreviated style to a normal sentence, aligning the message with those from fuel_gauge_read_*bit_word() Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 43cc171101f1..796153caf5e0 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -142,7 +142,7 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) } if (ret < 0) { - dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret); + dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); return ret; } @@ -156,7 +156,7 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val) ret = regmap_write(info->regmap, reg, (unsigned int)val); if (ret < 0) - dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret); + dev_err(&info->pdev->dev, "Error writing reg 0x%02x err: %d\n", reg, ret); return ret; } From f17bda7f655fa5ede982a487b8f6d732bbe1959a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:31:00 +0200 Subject: [PATCH 090/177] power: supply: axp288_fuel_gauge: Drop retry logic from fuel_gauge_reg_readb() The I2C-bus to the XPower AXP288 is shared between the Linux kernel and the SoCs P-Unit. The P-Unit has a semaphore which the kernel must "lock" before it may use the bus. This semaphore is automatically taken by the I2C-bus-driver. The retry on -EBUSY logic in fuel_gauge_reg_readb() likely was added to deal with the I2C-bus-drive returning -EBUSY when it failed to take the semaphore, but this really should never happen. The semaphore code even has a WARN_ON(ret) to log a kernel backtrace if this does somehow happen, when this happens something is seriously wrong and the system typically freezes soon afterwards. TL;DR: the regmap_read() should never fail with -EBUSY so the retries are unnecessary. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 796153caf5e0..d58a1f81fcea 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -76,7 +76,6 @@ #define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */ #define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */ -#define NR_RETRY_CNT 3 #define DEV_NAME "axp288_fuel_gauge" /* 1.1mV per LSB expressed in uV */ @@ -132,15 +131,10 @@ static enum power_supply_property fuel_gauge_props[] = { static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) { - int ret, i; unsigned int val; + int ret; - for (i = 0; i < NR_RETRY_CNT; i++) { - ret = regmap_read(info->regmap, reg, &val); - if (ret != -EBUSY) - break; - } - + ret = regmap_read(info->regmap, reg, &val); if (ret < 0) { dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); return ret; From 7eef3e663834476946ecd4a43b4e1a2cb2bc9884 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:31:01 +0200 Subject: [PATCH 091/177] power: supply: axp288_fuel_gauge: Store struct device pointer in axp288_fg_info Directly store the struct device pointer in axp288_fg_info, rather then storing a pointer to the struct platform_device there and then using "&info->pdev->dev" everywhere. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 59 ++++++++++-------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index d58a1f81fcea..1366027edf49 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -102,7 +102,7 @@ enum { }; struct axp288_fg_info { - struct platform_device *pdev; + struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; int irq[AXP288_FG_INTR_NUM]; @@ -136,7 +136,7 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) ret = regmap_read(info->regmap, reg, &val); if (ret < 0) { - dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); + dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); return ret; } @@ -150,7 +150,7 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val) ret = regmap_write(info->regmap, reg, (unsigned int)val); if (ret < 0) - dev_err(&info->pdev->dev, "Error writing reg 0x%02x err: %d\n", reg, ret); + dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret); return ret; } @@ -162,15 +162,13 @@ static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg) ret = regmap_bulk_read(info->regmap, reg, buf, 2); if (ret < 0) { - dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", - reg, ret); + dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); return ret; } ret = get_unaligned_be16(buf); if (!(ret & FG_15BIT_WORD_VALID)) { - dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n", - reg); + dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg); return -ENXIO; } @@ -184,8 +182,7 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) ret = regmap_bulk_read(info->regmap, reg, buf, 2); if (ret < 0) { - dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", - reg, ret); + dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); return ret; } @@ -199,8 +196,7 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); if (pwr_stat < 0) { - dev_err(&info->pdev->dev, - "PWR STAT read failed:%d\n", pwr_stat); + dev_err(info->dev, "PWR STAT read failed: %d\n", pwr_stat); return; } @@ -210,7 +206,7 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); if (fg_res < 0) { - dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res); + dev_err(info->dev, "FG RES read failed: %d\n", fg_res); return; } if (!(fg_res & FG_REP_CAP_VALID)) @@ -232,7 +228,7 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr); if (ret < 0) { - dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret); + dev_err(info->dev, "FG get current failed: %d\n", ret); return; } if (curr == 0) { @@ -355,8 +351,7 @@ static int fuel_gauge_get_property(struct power_supply *ps, goto fuel_gauge_read_err; if (!(ret & FG_REP_CAP_VALID)) - dev_err(&info->pdev->dev, - "capacity measurement not valid\n"); + dev_err(info->dev, "capacity measurement not valid\n"); val->intval = (ret & FG_REP_CAP_VAL_MASK); break; case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: @@ -455,35 +450,31 @@ static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev) } if (i >= AXP288_FG_INTR_NUM) { - dev_warn(&info->pdev->dev, "spurious interrupt!!\n"); + dev_warn(info->dev, "spurious interrupt!!\n"); return IRQ_NONE; } switch (i) { case QWBTU_IRQ: - dev_info(&info->pdev->dev, - "Quit Battery under temperature in work mode IRQ (QWBTU)\n"); + dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n"); break; case WBTU_IRQ: - dev_info(&info->pdev->dev, - "Battery under temperature in work mode IRQ (WBTU)\n"); + dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n"); break; case QWBTO_IRQ: - dev_info(&info->pdev->dev, - "Quit Battery over temperature in work mode IRQ (QWBTO)\n"); + dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n"); break; case WBTO_IRQ: - dev_info(&info->pdev->dev, - "Battery over temperature in work mode IRQ (WBTO)\n"); + dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n"); break; case WL2_IRQ: - dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n"); + dev_info(info->dev, "Low Batt Warning(2) INTR\n"); break; case WL1_IRQ: - dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n"); + dev_info(info->dev, "Low Batt Warning(1) INTR\n"); break; default: - dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); + dev_warn(info->dev, "Spurious Interrupt!!!\n"); } power_supply_changed(info->bat); @@ -508,16 +499,15 @@ static const struct power_supply_desc fuel_gauge_desc = { .external_power_changed = fuel_gauge_external_power_changed, }; -static void fuel_gauge_init_irq(struct axp288_fg_info *info) +static void fuel_gauge_init_irq(struct axp288_fg_info *info, struct platform_device *pdev) { int ret, i, pirq; for (i = 0; i < AXP288_FG_INTR_NUM; i++) { - pirq = platform_get_irq(info->pdev, i); + pirq = platform_get_irq(pdev, i); info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) { - dev_warn(&info->pdev->dev, - "regmap_irq get virq failed for IRQ %d: %d\n", + dev_warn(info->dev, "regmap_irq get virq failed for IRQ %d: %d\n", pirq, info->irq[i]); info->irq[i] = -1; goto intr_failed; @@ -526,8 +516,7 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info) NULL, fuel_gauge_thread_handler, IRQF_ONESHOT, DEV_NAME, info); if (ret) { - dev_warn(&info->pdev->dev, - "request irq failed for IRQ %d: %d\n", + dev_warn(info->dev, "request irq failed for IRQ %d: %d\n", pirq, info->irq[i]); info->irq[i] = -1; goto intr_failed; @@ -649,7 +638,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - info->pdev = pdev; + info->dev = &pdev->dev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; info->status = POWER_SUPPLY_STATUS_UNKNOWN; @@ -714,7 +703,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) goto out_free_iio_chan; } - fuel_gauge_init_irq(info); + fuel_gauge_init_irq(info, pdev); return 0; From c371d4491ba6356d5e437bd8cc8a72797f3e93bd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:31:02 +0200 Subject: [PATCH 092/177] power: supply: axp288_fuel_gauge: Only read PWR_OP_MODE, FG_LOW_CAP_REG regs once Accessing registers on the AXP288 is quite expensive, so we should avoid doing unnecessary accesses. The FG_LOW_CAP_REG never changes underneath us, so we only need to read it once. Devices with an AXP288 do not have user-replace (let alone hot-swappable) batteries and the only bit we care about in the PWR_OP_MODE register is the CHRG_STAT_BAT_PRESENT bit, so we can get away with only reading the PWR_OP_MODE register once too. Note that the FG_LOW_CAP_REG is not marked volatile in the regmap, so we were effectively already reading it once. This change makes this explicit, this is done as preparation of a further patch which moves all remaining register accesses in fuel_gauge_get_property() out of that function. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 1366027edf49..8011628d3704 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -111,6 +111,8 @@ struct axp288_fg_info { struct mutex lock; int status; int max_volt; + int pwr_op; + int low_cap; struct dentry *debug_file; }; @@ -336,11 +338,7 @@ static int fuel_gauge_get_property(struct power_supply *ps, val->intval = PROP_CURR(value); break; case POWER_SUPPLY_PROP_PRESENT: - ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE); - if (ret < 0) - goto fuel_gauge_read_err; - - if (ret & CHRG_STAT_BAT_PRESENT) + if (info->pwr_op & CHRG_STAT_BAT_PRESENT) val->intval = 1; else val->intval = 0; @@ -355,10 +353,7 @@ static int fuel_gauge_get_property(struct power_supply *ps, val->intval = (ret & FG_REP_CAP_VAL_MASK); break; case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: - ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); - if (ret < 0) - goto fuel_gauge_read_err; - val->intval = (ret & 0x0f); + val->intval = (info->low_cap & 0x0f); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; @@ -398,7 +393,7 @@ static int fuel_gauge_set_property(struct power_supply *ps, const union power_supply_propval *val) { struct axp288_fg_info *info = power_supply_get_drvdata(ps); - int ret = 0; + int new_low_cap, ret = 0; mutex_lock(&info->lock); switch (prop) { @@ -407,12 +402,12 @@ static int fuel_gauge_set_property(struct power_supply *ps, ret = -EINVAL; break; } - ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); - if (ret < 0) - break; - ret &= 0xf0; - ret |= (val->intval & 0xf); - ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret); + new_low_cap = info->low_cap; + new_low_cap &= 0xf0; + new_low_cap |= (val->intval & 0xf); + ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap); + if (ret == 0) + info->low_cap = new_low_cap; break; default: ret = -EINVAL; @@ -695,6 +690,16 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) break; } + ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE); + if (ret < 0) + goto out_free_iio_chan; + info->pwr_op = ret; + + ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); + if (ret < 0) + goto out_free_iio_chan; + info->low_cap = ret; + psy_cfg.drv_data = info; info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg); if (IS_ERR(info->bat)) { From 394088f0b0668a1972b35fb25c54dedd1e89da7e Mon Sep 17 00:00:00 2001 From: Andrejus Basovas Date: Sun, 1 Aug 2021 15:31:03 +0200 Subject: [PATCH 093/177] power: supply: axp288_fuel_gauge: Refresh all registers in one go The I2C-bus to the XPower AXP288 is shared between the Linux kernel and the SoCs P-Unit. The P-Unit has a semaphore which the kernel must "lock" before it may use the bus and while the kernel holds the semaphore the CPU and GPU power-states must not be changed otherwise the system will freeze. This is a complex process, which is quite expensive. This is all done by iosf_mbi_block_punit_i2c_access(). To ensure that no unguarded I2C-bus accesses happen, iosf_mbi_block_punit_i2c_access() gets called by the I2C-bus-driver for every I2C transfer. Because this is so expensive it is allowed to call iosf_mbi_block_punit_i2c_access() in a nested fashion, so that higher-level code which does multiple I2C-transfers can call it once for a group of transfers, turning the calls done by the I2C-bus-driver into no-ops. Userspace power-supply API users typically will read all provided properties in one go, refreshing the last read values when power_supply_changed() is called by the driver and/or periodically (e.g. every 2 minutes). The reading of all properties in one go causes the P-Unit semaphore to quickly be taken and released multiple times in a row. Certain PMIC registers like AXP20X_FG_RES are even used in multiple properties so they get read multiple times, leading to a P-Unit take + release each time the register is read. As already mentioned the taking of the P-Unit semaphore is a quite expensive operation and it has also been reported that the "hammering" of the P-Unit semaphore done by the axp288_fuel_gauge driver can even cause stability issues with the system as a whole. Switch over to a scheme where the axp288_fuel_gauge driver keeps a local copy of all the registers which it uses for properties and make it only refresh its copy of the registers if the values are older then 1 minute; or when a fuel-gauge interrupt has triggered since the last read. This not only reduces the amount of reads, it also makes the code do all the reads in one go, rather then reading specific registers based on which property is being queried. This allows calling iosf_mbi_block_punit_i2c_access() once before doing all the reads, so that we now only take the P-Unit semaphore once per update. Tested-by: Andrejus Basovas Signed-off-by: Andrejus Basovas Co-developed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 2 +- drivers/power/supply/axp288_fuel_gauge.c | 203 ++++++++++++----------- 2 files changed, 107 insertions(+), 98 deletions(-) diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 47b7d2111c4e..331e1160e411 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -358,7 +358,7 @@ config AXP288_CHARGER config AXP288_FUEL_GAUGE tristate "X-Powers AXP288 Fuel Gauge" - depends on MFD_AXP20X && IIO + depends on MFD_AXP20X && IIO && IOSF_MBI help Say yes here to have support for X-Power power management IC (PMIC) Fuel Gauge. The device provides battery statistics and status diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 8011628d3704..8db8ab0827e4 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -2,7 +2,8 @@ /* * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver * - * Copyright (C) 2016-2017 Hans de Goede + * Copyright (C) 2020-2021 Andrejus Basovas + * Copyright (C) 2016-2021 Hans de Goede * Copyright (C) 2014 Intel Corporation * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -20,6 +21,7 @@ #include #include #include +#include #define PS_STAT_VBUS_TRIGGER (1 << 0) #define PS_STAT_BAT_CHRG_DIR (1 << 2) @@ -84,6 +86,7 @@ #define PROP_VOLT(a) ((a) * 1000) #define PROP_CURR(a) ((a) * 1000) +#define AXP288_REG_UPDATE_INTERVAL (60 * HZ) #define AXP288_FG_INTR_NUM 6 enum { QWBTU_IRQ = 0, @@ -114,6 +117,18 @@ struct axp288_fg_info { int pwr_op; int low_cap; struct dentry *debug_file; + + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + int pwr_stat; + int fg_res; + int bat_volt; + int d_curr; + int c_curr; + int ocv; + int fg_cc_mtr1; + int fg_des_cap1; }; static enum power_supply_property fuel_gauge_props[] = { @@ -192,25 +207,78 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); } +static int fuel_gauge_update_registers(struct axp288_fg_info *info) +{ + int ret; + + if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL)) + return 0; + + dev_dbg(info->dev, "Fuel Gauge updating register values...\n"); + + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + + ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); + if (ret < 0) + goto out; + info->pwr_stat = ret; + + ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); + if (ret < 0) + goto out; + info->fg_res = ret; + + ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt); + if (ret < 0) + goto out; + + if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) { + info->d_curr = 0; + ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr); + if (ret < 0) + goto out; + } else { + info->c_curr = 0; + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr); + if (ret < 0) + goto out; + } + + ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG); + if (ret < 0) + goto out; + info->ocv = ret; + + ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG); + if (ret < 0) + goto out; + info->fg_cc_mtr1 = ret; + + ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG); + if (ret < 0) + goto out; + info->fg_des_cap1 = ret; + + info->last_updated = jiffies; + info->valid = 1; + ret = 0; +out: + iosf_mbi_unblock_punit_i2c_access(); + return ret; +} + static void fuel_gauge_get_status(struct axp288_fg_info *info) { - int pwr_stat, fg_res, curr, ret; - - pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); - if (pwr_stat < 0) { - dev_err(info->dev, "PWR STAT read failed: %d\n", pwr_stat); - return; - } + int pwr_stat = info->pwr_stat; + int fg_res = info->fg_res; + int curr = info->d_curr; /* Report full if Vbus is valid and the reported capacity is 100% */ if (!(pwr_stat & PS_STAT_VBUS_VALID)) goto not_full; - fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); - if (fg_res < 0) { - dev_err(info->dev, "FG RES read failed: %d\n", fg_res); - return; - } if (!(fg_res & FG_REP_CAP_VALID)) goto not_full; @@ -228,11 +296,6 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR)) goto not_full; - ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr); - if (ret < 0) { - dev_err(info->dev, "FG get current failed: %d\n", ret); - return; - } if (curr == 0) { info->status = POWER_SUPPLY_STATUS_FULL; return; @@ -245,61 +308,16 @@ not_full: info->status = POWER_SUPPLY_STATUS_DISCHARGING; } -static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) -{ - int ret = 0, raw_val; - - ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); - if (ret < 0) - goto vbatt_read_fail; - - *vbatt = VOLTAGE_FROM_ADC(raw_val); -vbatt_read_fail: - return ret; -} - -static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) -{ - int ret, discharge; - - /* First check discharge current, so that we do only 1 read on bat. */ - ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); - if (ret < 0) - return ret; - - if (discharge > 0) { - *cur = -1 * discharge; - return 0; - } - - return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur); -} - -static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv) -{ - int ret; - - ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG); - if (ret >= 0) - *vocv = VOLTAGE_FROM_ADC(ret); - - return ret; -} - static int fuel_gauge_battery_health(struct axp288_fg_info *info) { - int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN; - - ret = fuel_gauge_get_vocv(info, &vocv); - if (ret < 0) - goto health_read_fail; + int vocv = VOLTAGE_FROM_ADC(info->ocv); + int health = POWER_SUPPLY_HEALTH_UNKNOWN; if (vocv > info->max_volt) health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; else health = POWER_SUPPLY_HEALTH_GOOD; -health_read_fail: return health; } @@ -308,9 +326,14 @@ static int fuel_gauge_get_property(struct power_supply *ps, union power_supply_propval *val) { struct axp288_fg_info *info = power_supply_get_drvdata(ps); - int ret = 0, value; + int ret, value; mutex_lock(&info->lock); + + ret = fuel_gauge_update_registers(info); + if (ret < 0) + goto out; + switch (prop) { case POWER_SUPPLY_PROP_STATUS: fuel_gauge_get_status(info); @@ -320,21 +343,19 @@ static int fuel_gauge_get_property(struct power_supply *ps, val->intval = fuel_gauge_battery_health(info); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = fuel_gauge_get_vbatt(info, &value); - if (ret < 0) - goto fuel_gauge_read_err; + value = VOLTAGE_FROM_ADC(info->bat_volt); val->intval = PROP_VOLT(value); break; case POWER_SUPPLY_PROP_VOLTAGE_OCV: - ret = fuel_gauge_get_vocv(info, &value); - if (ret < 0) - goto fuel_gauge_read_err; + value = VOLTAGE_FROM_ADC(info->ocv); val->intval = PROP_VOLT(value); break; case POWER_SUPPLY_PROP_CURRENT_NOW: - ret = fuel_gauge_get_current(info, &value); - if (ret < 0) - goto fuel_gauge_read_err; + if (info->d_curr > 0) + value = -1 * info->d_curr; + else + value = info->c_curr; + val->intval = PROP_CURR(value); break; case POWER_SUPPLY_PROP_PRESENT: @@ -344,13 +365,9 @@ static int fuel_gauge_get_property(struct power_supply *ps, val->intval = 0; break; case POWER_SUPPLY_PROP_CAPACITY: - ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); - if (ret < 0) - goto fuel_gauge_read_err; - - if (!(ret & FG_REP_CAP_VALID)) + if (!(info->fg_res & FG_REP_CAP_VALID)) dev_err(info->dev, "capacity measurement not valid\n"); - val->intval = (ret & FG_REP_CAP_VAL_MASK); + val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK); break; case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: val->intval = (info->low_cap & 0x0f); @@ -359,31 +376,19 @@ static int fuel_gauge_get_property(struct power_supply *ps, val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CHARGE_NOW: - ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG); - if (ret < 0) - goto fuel_gauge_read_err; - - val->intval = ret * FG_DES_CAP_RES_LSB; + val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG); - if (ret < 0) - goto fuel_gauge_read_err; - - val->intval = ret * FG_DES_CAP_RES_LSB; + val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: val->intval = PROP_VOLT(info->max_volt); break; default: - mutex_unlock(&info->lock); - return -EINVAL; + ret = -EINVAL; } - mutex_unlock(&info->lock); - return 0; - -fuel_gauge_read_err: +out: mutex_unlock(&info->lock); return ret; } @@ -472,6 +477,8 @@ static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev) dev_warn(info->dev, "Spurious Interrupt!!!\n"); } + info->valid = 0; /* Force updating of the cached registers */ + power_supply_changed(info->bat); return IRQ_HANDLED; } @@ -480,6 +487,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy) { struct axp288_fg_info *info = power_supply_get_drvdata(psy); + info->valid = 0; /* Force updating of the cached registers */ power_supply_changed(info->bat); } @@ -637,6 +645,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; info->status = POWER_SUPPLY_STATUS_UNKNOWN; + info->valid = 0; platform_set_drvdata(pdev, info); From 964b3e9b02bd89a17fdd108a2ecb053beba2b43f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:31:04 +0200 Subject: [PATCH 094/177] power: supply: axp288_fuel_gauge: Move the AXP20X_CC_CTRL check together with the other checks The I2C-bus to the XPower AXP288 is shared between the Linux kernel and the SoCs P-Unit. The P-Unit has a semaphore which the kernel must "lock" before it may use the bus. If not explicitly taken by the I2C-driver, then this semaphore is automatically taken by the I2C-bus-driver for each I2C-transfer. Move the AXP20X_CC_CTRL check done in probe() together with the other register-accesses done in probe, so that we can take the semaphore once for the entire set of register-accesses. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 8db8ab0827e4..016d8d6bec40 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -627,16 +627,6 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) if (dmi_check_system(axp288_no_battery_list)) return -ENODEV; - /* - * On some devices the fuelgauge and charger parts of the axp288 are - * not used, check that the fuelgauge is enabled (CC_CTRL != 0). - */ - ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); - if (ret < 0) - return ret; - if (val == 0) - return -ENODEV; - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -671,6 +661,18 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) } } + /* + * On some devices the fuelgauge and charger parts of the axp288 are + * not used, check that the fuelgauge is enabled (CC_CTRL != 0). + */ + ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); + if (ret < 0) + goto out_free_iio_chan; + if (val == 0) { + ret = -ENODEV; + goto out_free_iio_chan; + } + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); if (ret < 0) goto out_free_iio_chan; From 213e19d659f9bb891387f105281a63700594a3dd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Aug 2021 15:31:05 +0200 Subject: [PATCH 095/177] power: supply: axp288_fuel_gauge: Take the P-Unit semaphore only once during probe() The I2C-bus to the XPower AXP288 is shared between the Linux kernel and the SoCs P-Unit. The P-Unit has a semaphore which the kernel must "lock" before it may use the bus. If not explicitly taken by the I2C-driver, then this semaphore is automatically taken by the I2C-bus-driver for each I2C-transfer and this is a quite expensive operation. Explicitly take the semaphore in probe() around the register-accesses done during probe, so that this only needs to be done once, rather then once per register-access. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 016d8d6bec40..c1da217fdb0e 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -661,31 +661,35 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) } } + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + goto out_free_iio_chan; + /* * On some devices the fuelgauge and charger parts of the axp288 are * not used, check that the fuelgauge is enabled (CC_CTRL != 0). */ ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); if (ret < 0) - goto out_free_iio_chan; + goto unblock_punit_i2c_access; if (val == 0) { ret = -ENODEV; - goto out_free_iio_chan; + goto unblock_punit_i2c_access; } ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); if (ret < 0) - goto out_free_iio_chan; + goto unblock_punit_i2c_access; if (!(ret & FG_DES_CAP1_VALID)) { dev_err(&pdev->dev, "axp288 not configured by firmware\n"); ret = -ENODEV; - goto out_free_iio_chan; + goto unblock_punit_i2c_access; } ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); if (ret < 0) - goto out_free_iio_chan; + goto unblock_punit_i2c_access; switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { case CHRG_CCCV_CV_4100MV: info->max_volt = 4100; @@ -703,14 +707,20 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE); if (ret < 0) - goto out_free_iio_chan; + goto unblock_punit_i2c_access; info->pwr_op = ret; ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); if (ret < 0) - goto out_free_iio_chan; + goto unblock_punit_i2c_access; info->low_cap = ret; +unblock_punit_i2c_access: + iosf_mbi_unblock_punit_i2c_access(); + /* In case we arrive here by goto because of a register access error */ + if (ret < 0) + goto out_free_iio_chan; + psy_cfg.drv_data = info; info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg); if (IS_ERR(info->bat)) { From e759e1b95836ec59dbadd8b7e8a7762a3c96798a Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Tue, 27 Jul 2021 22:03:44 +0500 Subject: [PATCH 096/177] dt-bindings: power: supply: max17042: Document max77849-battery max77849 is a combined fuel-gauge, charger and MUIC device. Add it to the bindings documentation. Signed-off-by: Nikita Travkin Acked-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/maxim,max17042.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml index c70f05ea6d27..42ebf87d300b 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml @@ -19,6 +19,7 @@ properties: - maxim,max17047 - maxim,max17050 - maxim,max17055 + - maxim,max77849-battery reg: maxItems: 1 From 4415e4cea4e6db863829914a48b68b7797db2f59 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Tue, 27 Jul 2021 22:03:45 +0500 Subject: [PATCH 097/177] power: supply: max17042_battery: Add support for MAX77849 Fuel-Gauge MAX77849 is a combined fuel-gauge, charger and MUIC IC. Notably, fuel-gauge has dedicated i2c lines and seems to be fully compatible with max17047. Add new compatible for it reusing max17047 code paths. Signed-off-by: Nikita Travkin Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index ce2041b30a06..f28c90ea41b4 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1196,6 +1196,7 @@ static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17047" }, { .compatible = "maxim,max17050" }, { .compatible = "maxim,max17055" }, + { .compatible = "maxim,max77849-battery" }, { }, }; MODULE_DEVICE_TABLE(of, max17042_dt_match); @@ -1206,6 +1207,7 @@ static const struct i2c_device_id max17042_id[] = { { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, + { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); From 83abf9e150f36c6e03644c0608c5f60cd9661a6c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 21 Jul 2021 16:03:58 +0200 Subject: [PATCH 098/177] dt-bindings: power: supply: axp20x: Add AXP803 compatible The AXP803 compatible was introduced recently with a fallback to the AXP813, but it was never documented. Cc: Chen-Yu Tsai Cc: linux-pm@vger.kernel.org Cc: Sebastian Reichel Signed-off-by: Maxime Ripard Reviewed-by: Rob Herring Reviewed-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- .../supply/x-powers,axp20x-ac-power-supply.yaml | 11 +++++++---- .../x-powers,axp20x-battery-power-supply.yaml | 11 +++++++---- .../supply/x-powers,axp20x-usb-power-supply.yaml | 14 +++++++++----- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml index dcda6660b8ed..de6a23aee977 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml @@ -21,10 +21,13 @@ allOf: properties: compatible: - enum: - - x-powers,axp202-ac-power-supply - - x-powers,axp221-ac-power-supply - - x-powers,axp813-ac-power-supply + oneOf: + - const: x-powers,axp202-ac-power-supply + - const: x-powers,axp221-ac-power-supply + - items: + - const: x-powers,axp803-ac-power-supply + - const: x-powers,axp813-ac-power-supply + - const: x-powers,axp813-ac-power-supply required: - compatible diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml index 86e8a713d4e2..d1f0df123a5a 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml @@ -19,10 +19,13 @@ allOf: properties: compatible: - enum: - - x-powers,axp209-battery-power-supply - - x-powers,axp221-battery-power-supply - - x-powers,axp813-battery-power-supply + oneOf: + - const: x-powers,axp202-battery-power-supply + - const: x-powers,axp221-battery-power-supply + - items: + - const: x-powers,axp803-battery-power-supply + - const: x-powers,axp813-battery-power-supply + - const: x-powers,axp813-battery-power-supply required: - compatible diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml index 61f1b320c157..0c371b55c9e1 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml @@ -20,11 +20,15 @@ allOf: properties: compatible: - enum: - - x-powers,axp202-usb-power-supply - - x-powers,axp221-usb-power-supply - - x-powers,axp223-usb-power-supply - - x-powers,axp813-usb-power-supply + oneOf: + - enum: + - x-powers,axp202-usb-power-supply + - x-powers,axp221-usb-power-supply + - x-powers,axp223-usb-power-supply + - x-powers,axp813-usb-power-supply + - items: + - const: x-powers,axp803-usb-power-supply + - const: x-powers,axp813-usb-power-supply required: From cc2712f24e032f2bebf4355207638bf15ca676b5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 21 Jul 2021 16:03:59 +0200 Subject: [PATCH 099/177] dt-bindings: power: supply: axp20x-battery: Add AXP209 compatible The AXP209 compatible was used in Device Trees and the driver, but it was never documented. Cc: Chen-Yu Tsai Cc: linux-pm@vger.kernel.org Cc: Sebastian Reichel Signed-off-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../power/supply/x-powers,axp20x-battery-power-supply.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml index d1f0df123a5a..d055428ae39f 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml @@ -21,6 +21,7 @@ properties: compatible: oneOf: - const: x-powers,axp202-battery-power-supply + - const: x-powers,axp209-battery-power-supply - const: x-powers,axp221-battery-power-supply - items: - const: x-powers,axp803-battery-power-supply From 391719dce5ebd416347f35d7a136feb6a04cecb8 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Tue, 20 Jul 2021 22:15:39 +0800 Subject: [PATCH 100/177] power: supply: qcom_smbb: Remove superfluous error message In the probe function, when get irq failed, the function platform_get_irq_byname() logs an error message, so remove redundant message here. Co-developed-by: Zhang Shengju Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin Signed-off-by: Sebastian Reichel --- drivers/power/supply/qcom_smbb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index c890e1cec720..84cc9fba029d 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -929,11 +929,8 @@ static int smbb_charger_probe(struct platform_device *pdev) int irq; irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq '%s'\n", - smbb_charger_irqs[i].name); + if (irq < 0) return irq; - } smbb_charger_irqs[i].handler(irq, chg); From e11544d0cdc16d59a4685872db5d81cd521819d3 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 19 Jul 2021 09:20:18 +0200 Subject: [PATCH 101/177] power: supply: sbs-battery: relax voltage limit The Smart Battery Data Specification allows for values 0..65535 mV, there is no reason to limit the value to 20000. Signed-off-by: Matthias Schiffer Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f84dbaab283a..3d6b8247d450 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -102,7 +102,7 @@ static const struct chip_data { [REG_TEMPERATURE] = SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), [REG_VOLTAGE] = - SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), + SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 65535), [REG_CURRENT_NOW] = SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767), [REG_CURRENT_AVG] = From 6ea0126631b0c3fb03ad69832c409b00a250d8dd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 19 Jul 2021 09:20:19 +0200 Subject: [PATCH 102/177] power: supply: sbs-battery: add support for time_to_empty_now attribute As defined by the Smart Battery Data Specification. An _AVG suffix is added to the enum values REG_TIME_TO_EMPTY and REG_TIME_TO_FULL to make the distinction clear. Signed-off-by: Matthias Schiffer Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 3d6b8247d450..c4a95b01463a 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -31,8 +31,9 @@ enum { REG_CURRENT_AVG, REG_MAX_ERR, REG_CAPACITY, - REG_TIME_TO_EMPTY, - REG_TIME_TO_FULL, + REG_TIME_TO_EMPTY_NOW, + REG_TIME_TO_EMPTY_AVG, + REG_TIME_TO_FULL_AVG, REG_STATUS, REG_CAPACITY_LEVEL, REG_CYCLE_COUNT, @@ -119,9 +120,11 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), [REG_FULL_CHARGE_CAPACITY_CHARGE] = SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), - [REG_TIME_TO_EMPTY] = + [REG_TIME_TO_EMPTY_NOW] = + SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 0x11, 0, 65535), + [REG_TIME_TO_EMPTY_AVG] = SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), - [REG_TIME_TO_FULL] = + [REG_TIME_TO_FULL_AVG] = SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535), [REG_CHARGE_CURRENT] = SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535), @@ -165,6 +168,7 @@ static const enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_SERIAL_NUMBER, @@ -748,6 +752,7 @@ static void sbs_unit_adjustment(struct i2c_client *client, val->intval -= TEMP_KELVIN_TO_CELSIUS; break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: /* sbs provides time to empty and time to full in minutes. @@ -966,6 +971,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: From 27fdd3bbb7a13830c05b6bf777e45c392cb00f63 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 6 Aug 2021 19:10:51 +1000 Subject: [PATCH 103/177] regulator: sy7636a: Use the regmap directly Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20210806091058.141-6-alistair@alistair23.me Signed-off-by: Mark Brown --- drivers/regulator/sy7636a-regulator.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index 37bf2a3c06b7..8360b3947ead 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -14,7 +14,7 @@ #include struct sy7636a_data { - struct sy7636a *sy7636a; + struct regmap *regmap; struct gpio_desc *pgood_gpio; }; @@ -70,14 +70,14 @@ static const struct regulator_desc desc = { static int sy7636a_regulator_probe(struct platform_device *pdev) { - struct sy7636a *sy7636a = dev_get_drvdata(pdev->dev.parent); + struct regmap *regmap = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = { }; struct regulator_dev *rdev; struct gpio_desc *gdp; struct sy7636a_data *data; int ret; - if (!sy7636a) + if (!regmap) return -EPROBE_DEFER; gdp = devm_gpiod_get(pdev->dev.parent, "epd-pwr-good", GPIOD_IN); @@ -90,12 +90,12 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - data->sy7636a = sy7636a; + data->regmap = regmap; data->pgood_gpio = gdp; platform_set_drvdata(pdev, data); - ret = regmap_write(sy7636a->regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0); + ret = regmap_write(regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0); if (ret) { dev_err(pdev->dev.parent, "Failed to initialize regulator: %d\n", ret); return ret; @@ -103,8 +103,7 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.dev->of_node = pdev->dev.parent->of_node; - config.driver_data = sy7636a; - config.regmap = sy7636a->regmap; + config.regmap = regmap; rdev = devm_regulator_register(&pdev->dev, &desc, &config); if (IS_ERR(rdev)) { From e4bb903fda0e9bbafa1338dcd2ee5e4d3ccc50da Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 22:27:30 +0300 Subject: [PATCH 104/177] spi: tegra20-slink: Improve runtime PM usage The Tegra SPI driver supports runtime PM, which controls the clock enable state, but the clk is also enabled separately from the RPM at the driver probe time, and thus, stays always on. Fix it. Runtime PM now is always available on Tegra, hence there is no need to check the RPM presence in the driver anymore. Remove these checks. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210731192731.5869-1-digetx@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 73 +++++++++++---------------------- 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 6a726c95ac7a..501eca1d0f89 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1061,33 +1061,12 @@ static int tegra_slink_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can not get clock %d\n", ret); goto exit_free_master; } - ret = clk_prepare(tspi->clk); - if (ret < 0) { - dev_err(&pdev->dev, "Clock prepare failed %d\n", ret); - goto exit_free_master; - } - ret = clk_enable(tspi->clk); - if (ret < 0) { - dev_err(&pdev->dev, "Clock enable failed %d\n", ret); - goto exit_clk_unprepare; - } - - spi_irq = platform_get_irq(pdev, 0); - tspi->irq = spi_irq; - ret = request_threaded_irq(tspi->irq, tegra_slink_isr, - tegra_slink_isr_thread, IRQF_ONESHOT, - dev_name(&pdev->dev), tspi); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", - tspi->irq); - goto exit_clk_disable; - } tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi"); if (IS_ERR(tspi->rst)) { dev_err(&pdev->dev, "can not get reset\n"); ret = PTR_ERR(tspi->rst); - goto exit_free_irq; + goto exit_free_master; } tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; @@ -1095,7 +1074,7 @@ static int tegra_slink_probe(struct platform_device *pdev) ret = tegra_slink_init_dma_param(tspi, true); if (ret < 0) - goto exit_free_irq; + goto exit_free_master; ret = tegra_slink_init_dma_param(tspi, false); if (ret < 0) goto exit_rx_dma_free; @@ -1106,16 +1085,9 @@ static int tegra_slink_probe(struct platform_device *pdev) init_completion(&tspi->xfer_completion); pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = tegra_slink_runtime_resume(&pdev->dev); - if (ret) - goto exit_pm_disable; - } - - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); - pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } @@ -1123,33 +1095,43 @@ static int tegra_slink_probe(struct platform_device *pdev) udelay(2); reset_control_deassert(tspi->rst); + spi_irq = platform_get_irq(pdev, 0); + tspi->irq = spi_irq; + ret = request_threaded_irq(tspi->irq, tegra_slink_isr, + tegra_slink_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_pm_put; + } + tspi->def_command_reg = SLINK_M_S; tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); - pm_runtime_put(&pdev->dev); master->dev.of_node = pdev->dev.of_node; ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); - goto exit_pm_disable; + goto exit_free_irq; } + + pm_runtime_put(&pdev->dev); + return ret; +exit_free_irq: + free_irq(spi_irq, tspi); +exit_pm_put: + pm_runtime_put(&pdev->dev); exit_pm_disable: pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - tegra_slink_runtime_suspend(&pdev->dev); + tegra_slink_deinit_dma_param(tspi, false); exit_rx_dma_free: tegra_slink_deinit_dma_param(tspi, true); -exit_free_irq: - free_irq(spi_irq, tspi); -exit_clk_disable: - clk_disable(tspi->clk); -exit_clk_unprepare: - clk_unprepare(tspi->clk); exit_free_master: spi_master_put(master); return ret; @@ -1162,8 +1144,7 @@ static int tegra_slink_remove(struct platform_device *pdev) free_irq(tspi->irq, tspi); - clk_disable(tspi->clk); - clk_unprepare(tspi->clk); + pm_runtime_disable(&pdev->dev); if (tspi->tx_dma_chan) tegra_slink_deinit_dma_param(tspi, false); @@ -1171,10 +1152,6 @@ static int tegra_slink_remove(struct platform_device *pdev) if (tspi->rx_dma_chan) tegra_slink_deinit_dma_param(tspi, true); - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - tegra_slink_runtime_suspend(&pdev->dev); - return 0; } From 26c863418221344b1cfb8e6c11116b2b81144281 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 22:27:31 +0300 Subject: [PATCH 105/177] spi: tegra20-slink: Don't use resource-managed spi_register helper Don't use resource-managed spi_register helper to correct the driver removal order and make it to match the error unwinding order of the probe function. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210731192731.5869-2-digetx@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 501eca1d0f89..deff16ba6d58 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1112,7 +1112,7 @@ static int tegra_slink_probe(struct platform_device *pdev) tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); master->dev.of_node = pdev->dev.of_node; - ret = devm_spi_register_master(&pdev->dev, master); + ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); goto exit_free_irq; @@ -1142,6 +1142,8 @@ static int tegra_slink_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct tegra_slink_data *tspi = spi_master_get_devdata(master); + spi_unregister_master(master); + free_irq(tspi->irq, tspi); pm_runtime_disable(&pdev->dev); @@ -1152,6 +1154,8 @@ static int tegra_slink_remove(struct platform_device *pdev) if (tspi->rx_dma_chan) tegra_slink_deinit_dma_param(tspi, true); + spi_master_put(master); + return 0; } From d05aaa66ba3ca3fdc2b5cd774ff218deb238b352 Mon Sep 17 00:00:00 2001 From: Zhengxun Li Date: Wed, 4 Aug 2021 13:27:07 +0800 Subject: [PATCH 106/177] spi: mxic: patch for octal DTR mode support Driver patch for octal DTR mode support. Owing to the spi_mem_default_supports_op() is not support dtr operation. Based on commit <539cf68cd51b> (spi: spi-mem: add spi_mem_dtr_supports_op()) add spi_mem_dtr_supports_op() to support dtr and keep checking the buswidth and command bytes. Signed-off-by: Zhengxun Li Link: https://lore.kernel.org/r/1628054827-458-1-git-send-email-zhengxunli@mxic.com.tw Signed-off-by: Mark Brown --- drivers/spi/spi-mxic.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 96b418293bf2..32e757a04f14 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -335,8 +335,10 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, static bool mxic_spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - if (op->data.buswidth > 4 || op->addr.buswidth > 4 || - op->dummy.buswidth > 4 || op->cmd.buswidth > 4) + bool all_false; + + if (op->data.buswidth > 8 || op->addr.buswidth > 8 || + op->dummy.buswidth > 8 || op->cmd.buswidth > 8) return false; if (op->data.nbytes && op->dummy.nbytes && @@ -346,7 +348,13 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem, if (op->addr.nbytes > 7) return false; - return spi_mem_default_supports_op(mem, op); + all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr && + !op->data.dtr; + + if (all_false) + return spi_mem_default_supports_op(mem, op); + else + return spi_mem_dtr_supports_op(mem, op); } static int mxic_spi_mem_exec_op(struct spi_mem *mem, @@ -355,14 +363,15 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master); int nio = 1, i, ret; u32 ss_ctrl; - u8 addr[8]; - u8 opcode = op->cmd.opcode; + u8 addr[8], cmd[2]; ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz); if (ret) return ret; - if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) + if (mem->spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL)) + nio = 8; + else if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) nio = 4; else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) nio = 2; @@ -374,19 +383,25 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, mxic->regs + HC_CFG); writel(HC_EN_BIT, mxic->regs + HC_EN); - ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1); + ss_ctrl = OP_CMD_BYTES(op->cmd.nbytes) | + OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) | + (op->cmd.dtr ? OP_CMD_DDR : 0); if (op->addr.nbytes) ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) | - OP_ADDR_BUSW(fls(op->addr.buswidth) - 1); + OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) | + (op->addr.dtr ? OP_ADDR_DDR : 0); if (op->dummy.nbytes) ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes); if (op->data.nbytes) { - ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1); + ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) | + (op->data.dtr ? OP_DATA_DDR : 0); if (op->data.dir == SPI_MEM_DATA_IN) ss_ctrl |= OP_READ; + if (op->data.dtr) + ss_ctrl |= OP_DQS_EN; } writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select)); @@ -394,7 +409,10 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, mxic->regs + HC_CFG); - ret = mxic_spi_data_xfer(mxic, &opcode, NULL, 1); + for (i = 0; i < op->cmd.nbytes; i++) + cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1)); + + ret = mxic_spi_data_xfer(mxic, cmd, NULL, op->cmd.nbytes); if (ret) goto out; @@ -567,7 +585,8 @@ static int mxic_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_TX_DUAL | - SPI_RX_QUAD | SPI_TX_QUAD; + SPI_RX_QUAD | SPI_TX_QUAD | + SPI_RX_OCTAL | SPI_TX_OCTAL; mxic_spi_hw_init(mxic); From 5c842e51ac63130a1344650b0a95bdc398666947 Mon Sep 17 00:00:00 2001 From: Mason Zhang Date: Mon, 9 Aug 2021 13:59:12 +0800 Subject: [PATCH 107/177] spi: mediatek: fix build warnning in set cs timing this patch fixed the build warnning in set cs timing. Signed-off-by: Mason Zhang Link: https://lore.kernel.org/r/20210809055911.17538-1-Mason.Zhang@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 20569c4c1fb6..6cf1b8bb6feb 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -214,7 +214,7 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) struct spi_delay *cs_setup = &spi->cs_setup; struct spi_delay *cs_hold = &spi->cs_hold; struct spi_delay *cs_inactive = &spi->cs_inactive; - u16 setup, hold, inactive; + u32 setup, hold, inactive; u32 reg_val; int delay; @@ -239,8 +239,8 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) reg_val = readl(mdata->base + SPI_CFG0_REG); if (mdata->dev_comp->enhance_timing) { - hold = min(hold, 0xffff); - setup = min(setup, 0xffff); + hold = min_t(u32, hold, 0x10000); + setup = min_t(u32, setup, 0x10000); reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); reg_val |= (((hold - 1) & 0xffff) << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); @@ -248,8 +248,8 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) reg_val |= (((setup - 1) & 0xffff) << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); } else { - hold = min(hold, 0xff); - setup = min(setup, 0xff); + hold = min_t(u32, hold, 0x100); + setup = min_t(u32, setup, 0x100); reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET); reg_val |= (((hold - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET); @@ -258,7 +258,7 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) } writel(reg_val, mdata->base + SPI_CFG0_REG); - inactive = min(inactive, 0xff); + inactive = min_t(u32, inactive, 0x100); reg_val = readl(mdata->base + SPI_CFG1_REG); reg_val &= ~SPI_CFG1_CS_IDLE_MASK; reg_val |= (((inactive - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); From c576e0fcd6188d0edb50b0fb83f853433ef4819b Mon Sep 17 00:00:00 2001 From: Matthew Bobrowski Date: Sun, 8 Aug 2021 15:24:33 +1000 Subject: [PATCH 108/177] kernel/pid.c: remove static qualifier from pidfd_create() With the idea of returning pidfds from the fanotify API, we need to expose a mechanism for creating pidfds. We drop the static qualifier from pidfd_create() and add its declaration to linux/pid.h so that the pidfd_create() helper can be called from other kernel subsystems i.e. fanotify. Link: https://lore.kernel.org/r/0c68653ec32f1b7143301f0231f7ed14062fd82b.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski Acked-by: Christian Brauner Signed-off-by: Jan Kara --- include/linux/pid.h | 1 + kernel/pid.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/pid.h b/include/linux/pid.h index fa10acb8d6a4..af308e15f174 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -78,6 +78,7 @@ struct file; extern struct pid *pidfd_pid(const struct file *file); struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags); +int pidfd_create(struct pid *pid, unsigned int flags); static inline struct pid *get_pid(struct pid *pid) { diff --git a/kernel/pid.c b/kernel/pid.c index ebdf9c60cd0b..d3cd95b8b080 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -550,10 +550,12 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags) * Note, that this function can only be called after the fd table has * been unshared to avoid leaking the pidfd to the new process. * + * This symbol should not be explicitly exported to loadable modules. + * * Return: On success, a cloexec pidfd is returned. * On error, a negative errno number will be returned. */ -static int pidfd_create(struct pid *pid, unsigned int flags) +int pidfd_create(struct pid *pid, unsigned int flags) { int fd; From 490b9ba881e2c6337bb09b68010803ae98e59f4a Mon Sep 17 00:00:00 2001 From: Matthew Bobrowski Date: Sun, 8 Aug 2021 15:25:05 +1000 Subject: [PATCH 109/177] kernel/pid.c: implement additional checks upon pidfd_create() parameters By adding the pidfd_create() declaration to linux/pid.h, we effectively expose this function to the rest of the kernel. In order to avoid any unintended behavior, or set false expectations upon this function, ensure that constraints are forced upon each of the passed parameters. This includes the checking of whether the passed struct pid is a thread-group leader as pidfd creation is currently limited to such pid types. Link: https://lore.kernel.org/r/2e9b91c2d529d52a003b8b86c45f866153be9eb5.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski Acked-by: Christian Brauner Signed-off-by: Jan Kara --- kernel/pid.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/kernel/pid.c b/kernel/pid.c index d3cd95b8b080..efe87db44683 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -559,6 +559,12 @@ int pidfd_create(struct pid *pid, unsigned int flags) { int fd; + if (!pid || !pid_has_task(pid, PIDTYPE_TGID)) + return -EINVAL; + + if (flags & ~(O_NONBLOCK | O_RDWR | O_CLOEXEC)) + return -EINVAL; + fd = anon_inode_getfd("[pidfd]", &pidfd_fops, get_pid(pid), flags | O_RDWR | O_CLOEXEC); if (fd < 0) @@ -598,10 +604,7 @@ SYSCALL_DEFINE2(pidfd_open, pid_t, pid, unsigned int, flags) if (!p) return -ESRCH; - if (pid_has_task(p, PIDTYPE_TGID)) - fd = pidfd_create(p, flags); - else - fd = -EINVAL; + fd = pidfd_create(p, flags); put_pid(p); return fd; From d3424c9bac893bd06f38a20474cd622881d384ca Mon Sep 17 00:00:00 2001 From: Matthew Bobrowski Date: Sun, 8 Aug 2021 15:25:32 +1000 Subject: [PATCH 110/177] fanotify: minor cosmetic adjustments to fid labels With the idea to support additional info record types in the future i.e. fanotify_event_info_pidfd, it's a good idea to rename some of the labels assigned to some of the existing fid related functions, parameters, etc which more accurately represent the intent behind their usage. For example, copy_info_to_user() was defined with a generic function label, which arguably reads as being supportive of different info record types, however the parameter list for this function is explicitly tailored towards the creation and copying of the fanotify_event_info_fid records. This same point applies to the macro defined as FANOTIFY_INFO_HDR_LEN. With fanotify_event_info_len(), we change the parameter label so that the function implies that it can be extended to calculate the length for additional info record types. Link: https://lore.kernel.org/r/7c3ec33f3c718dac40764305d4d494d858f59c51.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski Reviewed-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 64864fb40b40..182fea255376 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -104,7 +104,7 @@ struct kmem_cache *fanotify_path_event_cachep __read_mostly; struct kmem_cache *fanotify_perm_event_cachep __read_mostly; #define FANOTIFY_EVENT_ALIGN 4 -#define FANOTIFY_INFO_HDR_LEN \ +#define FANOTIFY_FID_INFO_HDR_LEN \ (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle)) static int fanotify_fid_info_len(int fh_len, int name_len) @@ -114,10 +114,11 @@ static int fanotify_fid_info_len(int fh_len, int name_len) if (name_len) info_len += name_len + 1; - return roundup(FANOTIFY_INFO_HDR_LEN + info_len, FANOTIFY_EVENT_ALIGN); + return roundup(FANOTIFY_FID_INFO_HDR_LEN + info_len, + FANOTIFY_EVENT_ALIGN); } -static int fanotify_event_info_len(unsigned int fid_mode, +static int fanotify_event_info_len(unsigned int info_mode, struct fanotify_event *event) { struct fanotify_info *info = fanotify_event_info(event); @@ -128,7 +129,8 @@ static int fanotify_event_info_len(unsigned int fid_mode, if (dir_fh_len) { info_len += fanotify_fid_info_len(dir_fh_len, info->name_len); - } else if ((fid_mode & FAN_REPORT_NAME) && (event->mask & FAN_ONDIR)) { + } else if ((info_mode & FAN_REPORT_NAME) && + (event->mask & FAN_ONDIR)) { /* * With group flag FAN_REPORT_NAME, if name was not recorded in * event on a directory, we will report the name ".". @@ -303,9 +305,10 @@ static int process_access_response(struct fsnotify_group *group, return -ENOENT; } -static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, - int info_type, const char *name, size_t name_len, - char __user *buf, size_t count) +static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, + int info_type, const char *name, + size_t name_len, + char __user *buf, size_t count) { struct fanotify_event_info_fid info = { }; struct file_handle handle = { }; @@ -466,10 +469,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, if (fanotify_event_dir_fh_len(event)) { info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME : FAN_EVENT_INFO_TYPE_DFID; - ret = copy_info_to_user(fanotify_event_fsid(event), - fanotify_info_dir_fh(info), - info_type, fanotify_info_name(info), - info->name_len, buf, count); + ret = copy_fid_info_to_user(fanotify_event_fsid(event), + fanotify_info_dir_fh(info), + info_type, + fanotify_info_name(info), + info->name_len, buf, count); if (ret < 0) goto out_close_fd; @@ -515,9 +519,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, info_type = FAN_EVENT_INFO_TYPE_FID; } - ret = copy_info_to_user(fanotify_event_fsid(event), - fanotify_event_object_fh(event), - info_type, dot, dot_len, buf, count); + ret = copy_fid_info_to_user(fanotify_event_fsid(event), + fanotify_event_object_fh(event), + info_type, dot, dot_len, + buf, count); if (ret < 0) goto out_close_fd; From 0aca67bb7f0d8c997dfef8ff0bfeb0afb361f0e6 Mon Sep 17 00:00:00 2001 From: Matthew Bobrowski Date: Sun, 8 Aug 2021 15:25:58 +1000 Subject: [PATCH 111/177] fanotify: introduce a generic info record copying helper The copy_info_records_to_user() helper allows for the separation of info record copying routines/conditionals from copy_event_to_user(), which reduces the overall clutter within this function. This becomes especially true as we start introducing additional info records in the future i.e. struct fanotify_event_info_pidfd. On success, this helper returns the total amount of bytes that have been copied into the user supplied buffer and on error, a negative value is returned to the caller. The newly defined macro FANOTIFY_INFO_MODES can be used to obtain info record types that have been enabled for a specific notification group. This macro becomes useful in the subsequent patch when the FAN_REPORT_PIDFD initialization flag is introduced. Link: https://lore.kernel.org/r/8872947dfe12ce8ae6e9a7f2d49ea29bc8006af0.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski Reviewed-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify_user.c | 155 ++++++++++++++++------------- include/linux/fanotify.h | 2 + 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 182fea255376..99d145eaab49 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -173,7 +173,7 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group, size_t event_size = FAN_EVENT_METADATA_LEN; struct fanotify_event *event = NULL; struct fsnotify_event *fsn_event; - unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); + unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); pr_debug("%s: group=%p count=%zd\n", __func__, group, count); @@ -183,8 +183,8 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group, goto out; event = FANOTIFY_E(fsn_event); - if (fid_mode) - event_size += fanotify_event_info_len(fid_mode, event); + if (info_mode) + event_size += fanotify_event_info_len(info_mode, event); if (event_size > count) { event = ERR_PTR(-EINVAL); @@ -401,6 +401,86 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, return info_len; } +static int copy_info_records_to_user(struct fanotify_event *event, + struct fanotify_info *info, + unsigned int info_mode, + char __user *buf, size_t count) +{ + int ret, total_bytes = 0, info_type = 0; + unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS; + + /* + * Event info records order is as follows: dir fid + name, child fid. + */ + if (fanotify_event_dir_fh_len(event)) { + info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME : + FAN_EVENT_INFO_TYPE_DFID; + ret = copy_fid_info_to_user(fanotify_event_fsid(event), + fanotify_info_dir_fh(info), + info_type, + fanotify_info_name(info), + info->name_len, buf, count); + if (ret < 0) + return ret; + + buf += ret; + count -= ret; + total_bytes += ret; + } + + if (fanotify_event_object_fh_len(event)) { + const char *dot = NULL; + int dot_len = 0; + + if (fid_mode == FAN_REPORT_FID || info_type) { + /* + * With only group flag FAN_REPORT_FID only type FID is + * reported. Second info record type is always FID. + */ + info_type = FAN_EVENT_INFO_TYPE_FID; + } else if ((fid_mode & FAN_REPORT_NAME) && + (event->mask & FAN_ONDIR)) { + /* + * With group flag FAN_REPORT_NAME, if name was not + * recorded in an event on a directory, report the name + * "." with info type DFID_NAME. + */ + info_type = FAN_EVENT_INFO_TYPE_DFID_NAME; + dot = "."; + dot_len = 1; + } else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) || + (event->mask & FAN_ONDIR)) { + /* + * With group flag FAN_REPORT_DIR_FID, a single info + * record has type DFID for directory entry modification + * event and for event on a directory. + */ + info_type = FAN_EVENT_INFO_TYPE_DFID; + } else { + /* + * With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID, + * a single info record has type FID for event on a + * non-directory, when there is no directory to report. + * For example, on FAN_DELETE_SELF event. + */ + info_type = FAN_EVENT_INFO_TYPE_FID; + } + + ret = copy_fid_info_to_user(fanotify_event_fsid(event), + fanotify_event_object_fh(event), + info_type, dot, dot_len, + buf, count); + if (ret < 0) + return ret; + + buf += ret; + count -= ret; + total_bytes += ret; + } + + return total_bytes; +} + static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fanotify_event *event, char __user *buf, size_t count) @@ -408,15 +488,14 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fanotify_event_metadata metadata; struct path *path = fanotify_event_path(event); struct fanotify_info *info = fanotify_event_info(event); - unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); + unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); struct file *f = NULL; int ret, fd = FAN_NOFD; - int info_type = 0; pr_debug("%s: group=%p event=%p\n", __func__, group, event); metadata.event_len = FAN_EVENT_METADATA_LEN + - fanotify_event_info_len(fid_mode, event); + fanotify_event_info_len(info_mode, event); metadata.metadata_len = FAN_EVENT_METADATA_LEN; metadata.vers = FANOTIFY_METADATA_VERSION; metadata.reserved = 0; @@ -465,69 +544,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, if (f) fd_install(fd, f); - /* Event info records order is: dir fid + name, child fid */ - if (fanotify_event_dir_fh_len(event)) { - info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME : - FAN_EVENT_INFO_TYPE_DFID; - ret = copy_fid_info_to_user(fanotify_event_fsid(event), - fanotify_info_dir_fh(info), - info_type, - fanotify_info_name(info), - info->name_len, buf, count); + if (info_mode) { + ret = copy_info_records_to_user(event, info, info_mode, + buf, count); if (ret < 0) goto out_close_fd; - - buf += ret; - count -= ret; - } - - if (fanotify_event_object_fh_len(event)) { - const char *dot = NULL; - int dot_len = 0; - - if (fid_mode == FAN_REPORT_FID || info_type) { - /* - * With only group flag FAN_REPORT_FID only type FID is - * reported. Second info record type is always FID. - */ - info_type = FAN_EVENT_INFO_TYPE_FID; - } else if ((fid_mode & FAN_REPORT_NAME) && - (event->mask & FAN_ONDIR)) { - /* - * With group flag FAN_REPORT_NAME, if name was not - * recorded in an event on a directory, report the - * name "." with info type DFID_NAME. - */ - info_type = FAN_EVENT_INFO_TYPE_DFID_NAME; - dot = "."; - dot_len = 1; - } else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) || - (event->mask & FAN_ONDIR)) { - /* - * With group flag FAN_REPORT_DIR_FID, a single info - * record has type DFID for directory entry modification - * event and for event on a directory. - */ - info_type = FAN_EVENT_INFO_TYPE_DFID; - } else { - /* - * With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID, - * a single info record has type FID for event on a - * non-directory, when there is no directory to report. - * For example, on FAN_DELETE_SELF event. - */ - info_type = FAN_EVENT_INFO_TYPE_FID; - } - - ret = copy_fid_info_to_user(fanotify_event_fsid(event), - fanotify_event_object_fh(event), - info_type, dot, dot_len, - buf, count); - if (ret < 0) - goto out_close_fd; - - buf += ret; - count -= ret; } return metadata.event_len; diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index a16dbeced152..10a7e26ddba6 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -27,6 +27,8 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */ #define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME) +#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS) + /* * fanotify_init() flags that require CAP_SYS_ADMIN. * We do not allow unprivileged groups to request permission events. From af579beb666aefb17e9a335c12c788c92932baf1 Mon Sep 17 00:00:00 2001 From: Matthew Bobrowski Date: Sun, 8 Aug 2021 15:26:25 +1000 Subject: [PATCH 112/177] fanotify: add pidfd support to the fanotify API Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd information record containing a pidfd is to be returned alongside the generic event metadata for each event. If FAN_REPORT_PIDFD is enabled for a notification group, an additional struct fanotify_event_info_pidfd object type will be supplied alongside the generic struct fanotify_event_metadata for a single event. This functionality is analogous to that of FAN_REPORT_FID in terms of how the event structure is supplied to a userspace application. Usage of FAN_REPORT_PIDFD with FAN_REPORT_FID/FAN_REPORT_DFID_NAME is permitted, and in this case a struct fanotify_event_info_pidfd object will likely follow any struct fanotify_event_info_fid object. Currently, the usage of the FAN_REPORT_TID flag is not permitted along with FAN_REPORT_PIDFD as the pidfd API currently only supports the creation of pidfds for thread-group leaders. Additionally, usage of the FAN_REPORT_PIDFD flag is limited to privileged processes only i.e. event listeners that are running with the CAP_SYS_ADMIN capability. Attempting to supply the FAN_REPORT_TID initialization flags with FAN_REPORT_PIDFD or creating a notification group without CAP_SYS_ADMIN will result with -EINVAL being returned to the caller. In the event of a pidfd creation error, there are two types of error values that can be reported back to the listener. There is FAN_NOPIDFD, which will be reported in cases where the process responsible for generating the event has terminated prior to the event listener being able to read the event. Then there is FAN_EPIDFD, which will be reported when a more generic pidfd creation error has occurred when fanotify calls pidfd_create(). Link: https://lore.kernel.org/r/5f9e09cff7ed62bfaa51c1369e0f7ea5f16a91aa.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify_user.c | 85 ++++++++++++++++++++++++++++-- include/linux/fanotify.h | 3 +- include/uapi/linux/fanotify.h | 13 +++++ 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 99d145eaab49..084a83b1214d 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -106,6 +107,8 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly; #define FANOTIFY_EVENT_ALIGN 4 #define FANOTIFY_FID_INFO_HDR_LEN \ (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle)) +#define FANOTIFY_PIDFD_INFO_HDR_LEN \ + sizeof(struct fanotify_event_info_pidfd) static int fanotify_fid_info_len(int fh_len, int name_len) { @@ -138,6 +141,9 @@ static int fanotify_event_info_len(unsigned int info_mode, dot_len = 1; } + if (info_mode & FAN_REPORT_PIDFD) + info_len += FANOTIFY_PIDFD_INFO_HDR_LEN; + if (fh_len) info_len += fanotify_fid_info_len(fh_len, dot_len); @@ -401,13 +407,34 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, return info_len; } +static int copy_pidfd_info_to_user(int pidfd, + char __user *buf, + size_t count) +{ + struct fanotify_event_info_pidfd info = { }; + size_t info_len = FANOTIFY_PIDFD_INFO_HDR_LEN; + + if (WARN_ON_ONCE(info_len > count)) + return -EFAULT; + + info.hdr.info_type = FAN_EVENT_INFO_TYPE_PIDFD; + info.hdr.len = info_len; + info.pidfd = pidfd; + + if (copy_to_user(buf, &info, info_len)) + return -EFAULT; + + return info_len; +} + static int copy_info_records_to_user(struct fanotify_event *event, struct fanotify_info *info, - unsigned int info_mode, + unsigned int info_mode, int pidfd, char __user *buf, size_t count) { int ret, total_bytes = 0, info_type = 0; unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS; + unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD; /* * Event info records order is as follows: dir fid + name, child fid. @@ -478,6 +505,16 @@ static int copy_info_records_to_user(struct fanotify_event *event, total_bytes += ret; } + if (pidfd_mode) { + ret = copy_pidfd_info_to_user(pidfd, buf, count); + if (ret < 0) + return ret; + + buf += ret; + count -= ret; + total_bytes += ret; + } + return total_bytes; } @@ -489,8 +526,9 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, struct path *path = fanotify_event_path(event); struct fanotify_info *info = fanotify_event_info(event); unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); + unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD; struct file *f = NULL; - int ret, fd = FAN_NOFD; + int ret, pidfd = FAN_NOPIDFD, fd = FAN_NOFD; pr_debug("%s: group=%p event=%p\n", __func__, group, event); @@ -524,6 +562,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, } metadata.fd = fd; + if (pidfd_mode) { + /* + * Complain if the FAN_REPORT_PIDFD and FAN_REPORT_TID mutual + * exclusion is ever lifted. At the time of incoporating pidfd + * support within fanotify, the pidfd API only supported the + * creation of pidfds for thread-group leaders. + */ + WARN_ON_ONCE(FAN_GROUP_FLAG(group, FAN_REPORT_TID)); + + /* + * The PIDTYPE_TGID check for an event->pid is performed + * preemptively in an attempt to catch out cases where the event + * listener reads events after the event generating process has + * already terminated. Report FAN_NOPIDFD to the event listener + * in those cases, with all other pidfd creation errors being + * reported as FAN_EPIDFD. + */ + if (metadata.pid == 0 || + !pid_has_task(event->pid, PIDTYPE_TGID)) { + pidfd = FAN_NOPIDFD; + } else { + pidfd = pidfd_create(event->pid, 0); + if (pidfd < 0) + pidfd = FAN_EPIDFD; + } + } + ret = -EFAULT; /* * Sanity check copy size in case get_one_event() and @@ -545,7 +610,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, fd_install(fd, f); if (info_mode) { - ret = copy_info_records_to_user(event, info, info_mode, + ret = copy_info_records_to_user(event, info, info_mode, pidfd, buf, count); if (ret < 0) goto out_close_fd; @@ -558,6 +623,10 @@ out_close_fd: put_unused_fd(fd); fput(f); } + + if (pidfd >= 0) + close_fd(pidfd); + return ret; } @@ -1103,6 +1172,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) #endif return -EINVAL; + /* + * A pidfd can only be returned for a thread-group leader; thus + * FAN_REPORT_PIDFD and FAN_REPORT_TID need to remain mutually + * exclusive. + */ + if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID)) + return -EINVAL; + if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) return -EINVAL; @@ -1504,7 +1581,7 @@ static int __init fanotify_user_setup(void) FANOTIFY_DEFAULT_MAX_USER_MARKS); BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); - BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10); + BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11); BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9); fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 10a7e26ddba6..eec3b7c40811 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -27,7 +27,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */ #define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME) -#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS) +#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD) /* * fanotify_init() flags that require CAP_SYS_ADMIN. @@ -37,6 +37,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */ */ #define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \ FAN_REPORT_TID | \ + FAN_REPORT_PIDFD | \ FAN_UNLIMITED_QUEUE | \ FAN_UNLIMITED_MARKS) diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h index fbf9c5c7dd59..64553df9d735 100644 --- a/include/uapi/linux/fanotify.h +++ b/include/uapi/linux/fanotify.h @@ -51,6 +51,7 @@ #define FAN_ENABLE_AUDIT 0x00000040 /* Flags to determine fanotify event format */ +#define FAN_REPORT_PIDFD 0x00000080 /* Report pidfd for event->pid */ #define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */ #define FAN_REPORT_FID 0x00000200 /* Report unique file id */ #define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */ @@ -123,6 +124,7 @@ struct fanotify_event_metadata { #define FAN_EVENT_INFO_TYPE_FID 1 #define FAN_EVENT_INFO_TYPE_DFID_NAME 2 #define FAN_EVENT_INFO_TYPE_DFID 3 +#define FAN_EVENT_INFO_TYPE_PIDFD 4 /* Variable length info record following event metadata */ struct fanotify_event_info_header { @@ -148,6 +150,15 @@ struct fanotify_event_info_fid { unsigned char handle[0]; }; +/* + * This structure is used for info records of type FAN_EVENT_INFO_TYPE_PIDFD. + * It holds a pidfd for the pid that was responsible for generating an event. + */ +struct fanotify_event_info_pidfd { + struct fanotify_event_info_header hdr; + __s32 pidfd; +}; + struct fanotify_response { __s32 fd; __u32 response; @@ -160,6 +171,8 @@ struct fanotify_response { /* No fd set in event */ #define FAN_NOFD -1 +#define FAN_NOPIDFD FAN_NOFD +#define FAN_EPIDFD -2 /* Helper functions to deal with fanotify_event_metadata buffers */ #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) From 209ab223ad5b18e437289235e3bde12593b94ac4 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 10 Aug 2021 11:17:26 +0300 Subject: [PATCH 113/177] spi: spi-fsl-dspi: Fix issue with uninitialized dma_slave_config Depending on the DMA driver being used, the struct dma_slave_config may need to be initialized to zero for the unused data. For example, we have three DMA drivers using src_port_window_size and dst_port_window_size. If these are left uninitialized, it can cause DMA failures. For spi-fsl-dspi, this is probably not currently an issue but is still good to fix though. Fixes: 90ba37033cb9 ("spi: spi-fsl-dspi: Add DMA support for Vybrid") Cc: Sanchayan Maity Cc: Vladimir Oltean Cc: Peter Ujfalusi Cc: Vinod Koul Signed-off-by: Tony Lindgren Acked-by: Vladimir Oltean Link: https://lore.kernel.org/r/20210810081727.19491-1-tony@atomide.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index fb45e6af6638..fd004c9db9dc 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -530,6 +530,7 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr) goto err_rx_dma_buf; } + memset(&cfg, 0, sizeof(cfg)); cfg.src_addr = phy_addr + SPI_POPR; cfg.dst_addr = phy_addr + SPI_PUSHR; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; From 976c1de1de147bb7f4e0d87482f375221c05aeaf Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 10 Aug 2021 11:17:27 +0300 Subject: [PATCH 114/177] spi: spi-pic32: Fix issue with uninitialized dma_slave_config Depending on the DMA driver being used, the struct dma_slave_config may need to be initialized to zero for the unused data. For example, we have three DMA drivers using src_port_window_size and dst_port_window_size. If these are left uninitialized, it can cause DMA failures. For spi-pic32, this is probably not currently an issue but is still good to fix though. Fixes: 1bcb9f8ceb67 ("spi: spi-pic32: Add PIC32 SPI master driver") Cc: Purna Chandra Mandal Cc: Peter Ujfalusi Cc: Vinod Koul Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210810081727.19491-2-tony@atomide.com Signed-off-by: Mark Brown --- drivers/spi/spi-pic32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 104bde153efd..5eb7b61bbb4d 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -361,6 +361,7 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) struct dma_slave_config cfg; int ret; + memset(&cfg, 0, sizeof(cfg)); cfg.device_fc = true; cfg.src_addr = pic32s->dma_base + buf_offset; cfg.dst_addr = pic32s->dma_base + buf_offset; From 09ddbe69c9925b42cb9529f60678c25b241d8b18 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 10 Aug 2021 18:12:17 +0300 Subject: [PATCH 115/177] fsnotify: replace igrab() with ihold() on attach connector We must have a reference on inode, so ihold is cheaper. Link: https://lore.kernel.org/r/20210810151220.285179-2-amir73il@gmail.com Reviewed-by: Matthew Bobrowski Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/mark.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/notify/mark.c b/fs/notify/mark.c index d32ab349db74..80459db58f63 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -493,8 +493,11 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, conn->fsid.val[0] = conn->fsid.val[1] = 0; conn->flags = 0; } - if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) - inode = igrab(fsnotify_conn_inode(conn)); + if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { + inode = fsnotify_conn_inode(conn); + ihold(inode); + } + /* * cmpxchg() provides the barrier so that readers of *connp can see * only initialized structure From 11fa333b58ba1518e7c69fafb6513a0117f8fe33 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 10 Aug 2021 18:12:18 +0300 Subject: [PATCH 116/177] fsnotify: count s_fsnotify_inode_refs for attached connectors Instead of incrementing s_fsnotify_inode_refs when detaching connector from inode, increment it earlier when attaching connector to inode. Next patch is going to use s_fsnotify_inode_refs to count all objects with attached connectors. Link: https://lore.kernel.org/r/20210810151220.285179-3-amir73il@gmail.com Reviewed-by: Matthew Bobrowski Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/mark.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 80459db58f63..2d8c46e1167d 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -169,6 +169,21 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work) } } +static void fsnotify_get_inode_ref(struct inode *inode) +{ + ihold(inode); + atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs); +} + +static void fsnotify_put_inode_ref(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + iput(inode); + if (atomic_long_dec_and_test(&sb->s_fsnotify_inode_refs)) + wake_up_var(&sb->s_fsnotify_inode_refs); +} + static void *fsnotify_detach_connector_from_object( struct fsnotify_mark_connector *conn, unsigned int *type) @@ -182,7 +197,6 @@ static void *fsnotify_detach_connector_from_object( if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { inode = fsnotify_conn_inode(conn); inode->i_fsnotify_mask = 0; - atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs); } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { @@ -209,19 +223,12 @@ static void fsnotify_final_mark_destroy(struct fsnotify_mark *mark) /* Drop object reference originally held by a connector */ static void fsnotify_drop_object(unsigned int type, void *objp) { - struct inode *inode; - struct super_block *sb; - if (!objp) return; /* Currently only inode references are passed to be dropped */ if (WARN_ON_ONCE(type != FSNOTIFY_OBJ_TYPE_INODE)) return; - inode = objp; - sb = inode->i_sb; - iput(inode); - if (atomic_long_dec_and_test(&sb->s_fsnotify_inode_refs)) - wake_up_var(&sb->s_fsnotify_inode_refs); + fsnotify_put_inode_ref(objp); } void fsnotify_put_mark(struct fsnotify_mark *mark) @@ -495,7 +502,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, } if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { inode = fsnotify_conn_inode(conn); - ihold(inode); + fsnotify_get_inode_ref(inode); } /* @@ -505,7 +512,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, if (cmpxchg(connp, NULL, conn)) { /* Someone else created list structure for us */ if (inode) - iput(inode); + fsnotify_put_inode_ref(inode); kmem_cache_free(fsnotify_mark_connector_cachep, conn); } From ec44610fe2b86daef70f3f53f47d2a2542d7094f Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 10 Aug 2021 18:12:19 +0300 Subject: [PATCH 117/177] fsnotify: count all objects with attached connectors Rename s_fsnotify_inode_refs to s_fsnotify_connectors and count all objects with attached connectors, not only inodes with attached connectors. This will be used to optimize fsnotify() calls on sb without any type of marks. Link: https://lore.kernel.org/r/20210810151220.285179-4-amir73il@gmail.com Signed-off-by: Amir Goldstein Reviewed-by: Matthew Bobrowski Signed-off-by: Jan Kara --- fs/notify/fsnotify.c | 6 +++--- fs/notify/fsnotify.h | 15 +++++++++++++++ fs/notify/mark.c | 24 +++++++++++++++++++++--- include/linux/fs.h | 7 +++++-- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 30d422b8c0fc..963e6ce75b96 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -87,15 +87,15 @@ static void fsnotify_unmount_inodes(struct super_block *sb) if (iput_inode) iput(iput_inode); - /* Wait for outstanding inode references from connectors */ - wait_var_event(&sb->s_fsnotify_inode_refs, - !atomic_long_read(&sb->s_fsnotify_inode_refs)); } void fsnotify_sb_delete(struct super_block *sb) { fsnotify_unmount_inodes(sb); fsnotify_clear_marks_by_sb(sb); + /* Wait for outstanding object references from connectors */ + wait_var_event(&sb->s_fsnotify_connectors, + !atomic_long_read(&sb->s_fsnotify_connectors)); } /* diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index ff2063ec6b0f..87d8a50ee803 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -27,6 +27,21 @@ static inline struct super_block *fsnotify_conn_sb( return container_of(conn->obj, struct super_block, s_fsnotify_marks); } +static inline struct super_block *fsnotify_connector_sb( + struct fsnotify_mark_connector *conn) +{ + switch (conn->type) { + case FSNOTIFY_OBJ_TYPE_INODE: + return fsnotify_conn_inode(conn)->i_sb; + case FSNOTIFY_OBJ_TYPE_VFSMOUNT: + return fsnotify_conn_mount(conn)->mnt.mnt_sb; + case FSNOTIFY_OBJ_TYPE_SB: + return fsnotify_conn_sb(conn); + default: + return NULL; + } +} + /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 2d8c46e1167d..95006d1d29ab 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -172,7 +172,7 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work) static void fsnotify_get_inode_ref(struct inode *inode) { ihold(inode); - atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs); + atomic_long_inc(&inode->i_sb->s_fsnotify_connectors); } static void fsnotify_put_inode_ref(struct inode *inode) @@ -180,8 +180,24 @@ static void fsnotify_put_inode_ref(struct inode *inode) struct super_block *sb = inode->i_sb; iput(inode); - if (atomic_long_dec_and_test(&sb->s_fsnotify_inode_refs)) - wake_up_var(&sb->s_fsnotify_inode_refs); + if (atomic_long_dec_and_test(&sb->s_fsnotify_connectors)) + wake_up_var(&sb->s_fsnotify_connectors); +} + +static void fsnotify_get_sb_connectors(struct fsnotify_mark_connector *conn) +{ + struct super_block *sb = fsnotify_connector_sb(conn); + + if (sb) + atomic_long_inc(&sb->s_fsnotify_connectors); +} + +static void fsnotify_put_sb_connectors(struct fsnotify_mark_connector *conn) +{ + struct super_block *sb = fsnotify_connector_sb(conn); + + if (sb && atomic_long_dec_and_test(&sb->s_fsnotify_connectors)) + wake_up_var(&sb->s_fsnotify_connectors); } static void *fsnotify_detach_connector_from_object( @@ -203,6 +219,7 @@ static void *fsnotify_detach_connector_from_object( fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; } + fsnotify_put_sb_connectors(conn); rcu_assign_pointer(*(conn->obj), NULL); conn->obj = NULL; conn->type = FSNOTIFY_OBJ_TYPE_DETACHED; @@ -504,6 +521,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, inode = fsnotify_conn_inode(conn); fsnotify_get_inode_ref(inode); } + fsnotify_get_sb_connectors(conn); /* * cmpxchg() provides the barrier so that readers of *connp can see diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..bea8ec5c726c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1507,8 +1507,11 @@ struct super_block { /* Number of inodes with nlink == 0 but still referenced */ atomic_long_t s_remove_count; - /* Pending fsnotify inode refs */ - atomic_long_t s_fsnotify_inode_refs; + /* + * Number of inode/mount/sb objects that are being watched, note that + * inodes objects are currently double-accounted. + */ + atomic_long_t s_fsnotify_connectors; /* Being remounted read-only */ int s_readonly_remount; From e43de7f0862b8598cd1ef440e3b4701cd107ea40 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 10 Aug 2021 18:12:20 +0300 Subject: [PATCH 118/177] fsnotify: optimize the case of no marks of any type Add a simple check in the inline helpers to avoid calling fsnotify() and __fsnotify_parent() in case there are no marks of any type (inode/sb/mount) for an inode's sb, so there can be no objects of any type interested in the event. Link: https://lore.kernel.org/r/20210810151220.285179-5-amir73il@gmail.com Reviewed-by: Matthew Bobrowski Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara --- include/linux/fsnotify.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index f8acddcf54fb..12d3a7d308ab 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -30,6 +30,9 @@ static inline void fsnotify_name(struct inode *dir, __u32 mask, struct inode *child, const struct qstr *name, u32 cookie) { + if (atomic_long_read(&dir->i_sb->s_fsnotify_connectors) == 0) + return; + fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie); } @@ -41,6 +44,9 @@ static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, static inline void fsnotify_inode(struct inode *inode, __u32 mask) { + if (atomic_long_read(&inode->i_sb->s_fsnotify_connectors) == 0) + return; + if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; @@ -53,6 +59,9 @@ static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, { struct inode *inode = d_inode(dentry); + if (atomic_long_read(&inode->i_sb->s_fsnotify_connectors) == 0) + return 0; + if (S_ISDIR(inode->i_mode)) { mask |= FS_ISDIR; From 781d2a9a2fc7d0be53a072794dc03ef6de770f3d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 3 May 2021 11:39:03 +0200 Subject: [PATCH 119/177] udf: Check LVID earlier We were checking validity of LVID entries only when getting implementation use information from LVID in udf_sb_lvidiu(). However if the LVID is suitably corrupted, it can cause problems also to code such as udf_count_free() which doesn't use udf_sb_lvidiu(). So check validity of LVID already when loading it from the disk and just disable LVID altogether when it is not valid. Reported-by: syzbot+7fbfe5fed73ebb675748@syzkaller.appspotmail.com Signed-off-by: Jan Kara --- fs/udf/super.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 2f83c1204e20..1eeb75a1efd2 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -108,16 +108,10 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) return NULL; lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data; partnum = le32_to_cpu(lvid->numOfPartitions); - if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) - - offsetof(struct logicalVolIntegrityDesc, impUse)) / - (2 * sizeof(uint32_t)) < partnum) { - udf_err(sb, "Logical volume integrity descriptor corrupted " - "(numOfPartitions = %u)!\n", partnum); - return NULL; - } /* The offset is to skip freeSpaceTable and sizeTable arrays */ offset = partnum * 2 * sizeof(uint32_t); - return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]); + return (struct logicalVolIntegrityDescImpUse *) + (((uint8_t *)(lvid + 1)) + offset); } /* UDF filesystem type */ @@ -1542,6 +1536,7 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ struct udf_sb_info *sbi = UDF_SB(sb); struct logicalVolIntegrityDesc *lvid; int indirections = 0; + u32 parts, impuselen; while (++indirections <= UDF_MAX_LVID_NESTING) { final_bh = NULL; @@ -1568,15 +1563,27 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data; if (lvid->nextIntegrityExt.extLength == 0) - return; + goto check; loc = leea_to_cpu(lvid->nextIntegrityExt); } udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n", UDF_MAX_LVID_NESTING); +out_err: brelse(sbi->s_lvid_bh); sbi->s_lvid_bh = NULL; + return; +check: + parts = le32_to_cpu(lvid->numOfPartitions); + impuselen = le32_to_cpu(lvid->lengthOfImpUse); + if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize || + sizeof(struct logicalVolIntegrityDesc) + impuselen + + 2 * parts * sizeof(u32) > sb->s_blocksize) { + udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), " + "ignoring.\n", parts, impuselen); + goto out_err; + } } /* From 04e8ee504a677d07dd60f6c8aae912e4842301c8 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 3 May 2021 11:48:09 +0200 Subject: [PATCH 120/177] udf: Remove unused declaration Remove declaration of struct virtualAllocationTable15. It is unused. Signed-off-by: Jan Kara --- fs/udf/osta_udf.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h index 22bc4fb2feb9..1c83aeede52e 100644 --- a/fs/udf/osta_udf.h +++ b/fs/udf/osta_udf.h @@ -178,15 +178,6 @@ struct metadataPartitionMap { uint8_t reserved2[5]; } __packed; -/* Virtual Allocation Table (UDF 1.5 2.2.10) */ -struct virtualAllocationTable15 { - __le32 vatEntry[0]; - struct regid vatIdent; - __le32 previousVATICBLoc; -} __packed; - -#define ICBTAG_FILE_TYPE_VAT15 0x00U - /* Virtual Allocation Table (UDF 2.60 2.2.11) */ struct virtualAllocationTable20 { __le16 lengthHeader; From b3c8c9801eb9b8e0f73246b4b14efbde1a4c570c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 3 May 2021 11:50:38 +0200 Subject: [PATCH 121/177] udf: Get rid of 0-length arrays Declare variable length arrays using [] instead of the old-style declarations using arrays with 0 members. Also comment out entries in structures beyond the first variable length array (we still do keep them in comments as a reminder there are further entries in the structure behind the variable length array). Accessing such entries needs a careful offset math anyway so it is safer to not have them declared. Signed-off-by: Jan Kara --- fs/udf/ecma_167.h | 38 +++++++++++++++++++------------------- fs/udf/osta_udf.h | 13 ++++++------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h index 185c3e247648..c6bea5c9841a 100644 --- a/fs/udf/ecma_167.h +++ b/fs/udf/ecma_167.h @@ -307,14 +307,14 @@ struct logicalVolDesc { struct regid impIdent; uint8_t impUse[128]; struct extent_ad integritySeqExt; - uint8_t partitionMaps[0]; + uint8_t partitionMaps[]; } __packed; /* Generic Partition Map (ECMA 167r3 3/10.7.1) */ struct genericPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; - uint8_t partitionMapping[0]; + uint8_t partitionMapping[]; } __packed; /* Partition Map Type (ECMA 167r3 3/10.7.1.1) */ @@ -342,7 +342,7 @@ struct unallocSpaceDesc { struct tag descTag; __le32 volDescSeqNum; __le32 numAllocDescs; - struct extent_ad allocDescs[0]; + struct extent_ad allocDescs[]; } __packed; /* Terminating Descriptor (ECMA 167r3 3/10.9) */ @@ -360,9 +360,9 @@ struct logicalVolIntegrityDesc { uint8_t logicalVolContentsUse[32]; __le32 numOfPartitions; __le32 lengthOfImpUse; - __le32 freeSpaceTable[0]; - __le32 sizeTable[0]; - uint8_t impUse[0]; + __le32 freeSpaceTable[]; + /* __le32 sizeTable[]; */ + /* uint8_t impUse[]; */ } __packed; /* Integrity Type (ECMA 167r3 3/10.10.3) */ @@ -578,8 +578,8 @@ struct fileEntry { __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; - uint8_t extendedAttr[0]; - uint8_t allocDescs[0]; + uint8_t extendedAttr[]; + /* uint8_t allocDescs[]; */ } __packed; /* Permissions (ECMA 167r3 4/14.9.5) */ @@ -632,7 +632,7 @@ struct genericFormat { uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; - uint8_t attrData[0]; + uint8_t attrData[]; } __packed; /* Character Set Information (ECMA 167r3 4/14.10.3) */ @@ -643,7 +643,7 @@ struct charSetInfo { __le32 attrLength; __le32 escapeSeqLength; uint8_t charSetType; - uint8_t escapeSeq[0]; + uint8_t escapeSeq[]; } __packed; /* Alternate Permissions (ECMA 167r3 4/14.10.4) */ @@ -682,7 +682,7 @@ struct infoTimesExtAttr { __le32 attrLength; __le32 dataLength; __le32 infoTimeExistence; - uint8_t infoTimes[0]; + uint8_t infoTimes[]; } __packed; /* Device Specification (ECMA 167r3 4/14.10.7) */ @@ -694,7 +694,7 @@ struct deviceSpec { __le32 impUseLength; __le32 majorDeviceIdent; __le32 minorDeviceIdent; - uint8_t impUse[0]; + uint8_t impUse[]; } __packed; /* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ @@ -705,7 +705,7 @@ struct impUseExtAttr { __le32 attrLength; __le32 impUseLength; struct regid impIdent; - uint8_t impUse[0]; + uint8_t impUse[]; } __packed; /* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ @@ -716,7 +716,7 @@ struct appUseExtAttr { __le32 attrLength; __le32 appUseLength; struct regid appIdent; - uint8_t appUse[0]; + uint8_t appUse[]; } __packed; #define EXTATTR_CHAR_SET 1 @@ -733,7 +733,7 @@ struct unallocSpaceEntry { struct tag descTag; struct icbtag icbTag; __le32 lengthAllocDescs; - uint8_t allocDescs[0]; + uint8_t allocDescs[]; } __packed; /* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ @@ -741,7 +741,7 @@ struct spaceBitmapDesc { struct tag descTag; __le32 numOfBits; __le32 numOfBytes; - uint8_t bitmap[0]; + uint8_t bitmap[]; } __packed; /* Partition Integrity Entry (ECMA 167r3 4/14.13) */ @@ -780,7 +780,7 @@ struct pathComponent { uint8_t componentType; uint8_t lengthComponentIdent; __le16 componentFileVersionNum; - dchars componentIdent[0]; + dchars componentIdent[]; } __packed; /* File Entry (ECMA 167r3 4/14.17) */ @@ -809,8 +809,8 @@ struct extendedFileEntry { __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; - uint8_t extendedAttr[0]; - uint8_t allocDescs[0]; + uint8_t extendedAttr[]; + /* uint8_t allocDescs[]; */ } __packed; #endif /* _ECMA_167_H */ diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h index 1c83aeede52e..157de0ec0cd5 100644 --- a/fs/udf/osta_udf.h +++ b/fs/udf/osta_udf.h @@ -111,7 +111,7 @@ struct logicalVolIntegrityDescImpUse { __le16 minUDFReadRev; __le16 minUDFWriteRev; __le16 maxUDFWriteRev; - uint8_t impUse[0]; + uint8_t impUse[]; } __packed; /* Implementation Use Volume Descriptor (UDF 2.60 2.2.7) */ @@ -190,8 +190,8 @@ struct virtualAllocationTable20 { __le16 minUDFWriteRev; __le16 maxUDFWriteRev; __le16 reserved; - uint8_t impUse[0]; - __le32 vatEntry[0]; + uint8_t impUse[]; + /* __le32 vatEntry[]; */ } __packed; #define ICBTAG_FILE_TYPE_VAT20 0xF8U @@ -208,8 +208,7 @@ struct sparingTable { __le16 reallocationTableLen; __le16 reserved; __le32 sequenceNum; - struct sparingEntry - mapEntry[0]; + struct sparingEntry mapEntry[]; } __packed; /* Metadata File (and Metadata Mirror File) (UDF 2.60 2.2.13.1) */ @@ -232,7 +231,7 @@ struct allocDescImpUse { /* FreeEASpace (UDF 2.60 3.3.4.5.1.1) */ struct freeEaSpace { __le16 headerChecksum; - uint8_t freeEASpace[0]; + uint8_t freeEASpace[]; } __packed; /* DVD Copyright Management Information (UDF 2.60 3.3.4.5.1.2) */ @@ -256,7 +255,7 @@ struct LVExtensionEA { /* FreeAppEASpace (UDF 2.60 3.3.4.6.1) */ struct freeAppEASpace { __le16 headerChecksum; - uint8_t freeEASpace[0]; + uint8_t freeEASpace[]; } __packed; /* UDF Defined System Stream (UDF 2.60 3.3.7) */ From 979a6e28dd969a2222545001f79566b4bfaf06c0 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 3 May 2021 11:54:24 +0200 Subject: [PATCH 122/177] udf: Get rid of 0-length arrays in struct fileIdentDesc Get rid of 0-length arrays in struct fileIdentDesc. This requires a bit of cleaning up as the second variable length array in this structure is often used and the code abuses the fact that the first two arrays have the same type and offset in struct fileIdentDesc. Signed-off-by: Jan Kara --- fs/udf/dir.c | 5 ++--- fs/udf/ecma_167.h | 6 +++--- fs/udf/inode.c | 3 +-- fs/udf/namei.c | 13 ++++++------- fs/udf/udfdecl.h | 4 ++++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/fs/udf/dir.c b/fs/udf/dir.c index c19dba45aa20..70abdfad2df1 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -35,7 +35,6 @@ #include "udf_i.h" #include "udf_sb.h" - static int udf_readdir(struct file *file, struct dir_context *ctx) { struct inode *dir = file_inode(file); @@ -135,7 +134,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) lfi = cfi.lengthFileIdent; if (fibh.sbh == fibh.ebh) { - nameptr = fi->fileIdent + liu; + nameptr = udf_get_fi_ident(fi); } else { int poffset; /* Unpaded ending offset */ @@ -153,7 +152,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) } } nameptr = copy_name; - memcpy(nameptr, fi->fileIdent + liu, + memcpy(nameptr, udf_get_fi_ident(fi), lfi - poffset); memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h index c6bea5c9841a..de17a97e8667 100644 --- a/fs/udf/ecma_167.h +++ b/fs/udf/ecma_167.h @@ -471,9 +471,9 @@ struct fileIdentDesc { uint8_t lengthFileIdent; struct long_ad icb; __le16 lengthOfImpUse; - uint8_t impUse[0]; - uint8_t fileIdent[0]; - uint8_t padding[0]; + uint8_t impUse[]; + /* uint8_t fileIdent[]; */ + /* uint8_t padding[]; */ } __packed; /* File Characteristics (ECMA 167r3 4/14.4.3) */ diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 4917670860a0..1d6b7a50736b 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -390,8 +390,7 @@ struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, - sfi->fileIdent + - le16_to_cpu(sfi->lengthOfImpUse))) { + udf_get_fi_ident(sfi))) { iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; brelse(dbh); return NULL; diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 7c7c9bbbfa57..caeef08efed2 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -74,12 +74,11 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, if (fileident) { if (adinicb || (offset + lfi < 0)) { - memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); + memcpy(udf_get_fi_ident(sfi), fileident, lfi); } else if (offset >= 0) { memcpy(fibh->ebh->b_data + offset, fileident, lfi); } else { - memcpy((uint8_t *)sfi->fileIdent + liu, fileident, - -offset); + memcpy(udf_get_fi_ident(sfi), fileident, -offset); memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); } @@ -88,11 +87,11 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, offset += lfi; if (adinicb || (offset + padlen < 0)) { - memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); + memset(udf_get_fi_ident(sfi) + lfi, 0x00, padlen); } else if (offset >= 0) { memset(fibh->ebh->b_data + offset, 0x00, padlen); } else { - memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); + memset(udf_get_fi_ident(sfi) + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } @@ -226,7 +225,7 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) { - nameptr = fi->fileIdent + liu; + nameptr = udf_get_fi_ident(fi); } else { int poffset; /* Unpaded ending offset */ @@ -246,7 +245,7 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, } } nameptr = copy_name; - memcpy(nameptr, fi->fileIdent + liu, + memcpy(nameptr, udf_get_fi_ident(fi), lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 9dd0814f1077..7e258f15b8ef 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -130,6 +130,10 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi) le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent, UDF_NAME_PAD); } +static inline uint8_t *udf_get_fi_ident(struct fileIdentDesc *fi) +{ + return ((uint8_t *)(fi + 1)) + le16_to_cpu(fi->lengthOfImpUse); +} /* file.c */ extern long udf_ioctl(struct file *, unsigned int, unsigned long); From aca196842a9729a198af57c417725c3ac9ca05db Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 10 Aug 2021 22:24:05 +0800 Subject: [PATCH 123/177] spi: mxic: add missing braces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following waring: drivers/spi/spi-mxic.c: In function ‘mxic_spi_mem_exec_op’: drivers/spi/spi-mxic.c:401:3: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation] if (op->data.dir == SPI_MEM_DATA_IN) ^~ drivers/spi/spi-mxic.c:403:4: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’ if (op->data.dtr) ^~ Signed-off-by: Yang Yingliang Reviewed-by: Zhengxun Li Link: https://lore.kernel.org/r/20210810142405.2221540-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mxic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 32e757a04f14..45889947afed 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -398,10 +398,11 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, if (op->data.nbytes) { ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) | (op->data.dtr ? OP_DATA_DDR : 0); - if (op->data.dir == SPI_MEM_DATA_IN) + if (op->data.dir == SPI_MEM_DATA_IN) { ss_ctrl |= OP_READ; if (op->data.dtr) ss_ctrl |= OP_DQS_EN; + } } writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select)); From b645333443712d2613e4e863f81090d5dc509657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sun, 8 Aug 2021 18:24:36 +0200 Subject: [PATCH 124/177] udf: Fix iocharset=utf8 mount option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently iocharset=utf8 mount option is broken. To use UTF-8 as iocharset, it is required to use utf8 mount option. Fix iocharset=utf8 mount option to use be equivalent to the utf8 mount option. If UTF-8 as iocharset is used then s_nls_map is set to NULL. So simplify code around, remove UDF_FLAG_NLS_MAP and UDF_FLAG_UTF8 flags as to distinguish between UTF-8 and non-UTF-8 it is needed just to check if s_nls_map set to NULL or not. Link: https://lore.kernel.org/r/20210808162453.1653-4-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Jan Kara --- fs/udf/super.c | 50 ++++++++++++++++++------------------------------ fs/udf/udf_sb.h | 2 -- fs/udf/unicode.c | 4 ++-- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 1eeb75a1efd2..b2d7c57d0688 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -343,10 +343,10 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",lastblock=%u", sbi->s_last_block); if (sbi->s_anchor != 0) seq_printf(seq, ",anchor=%u", sbi->s_anchor); - if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) - seq_puts(seq, ",utf8"); - if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP) && sbi->s_nls_map) + if (sbi->s_nls_map) seq_printf(seq, ",iocharset=%s", sbi->s_nls_map->charset); + else + seq_puts(seq, ",iocharset=utf8"); return 0; } @@ -552,19 +552,24 @@ static int udf_parse_options(char *options, struct udf_options *uopt, /* Ignored (never implemented properly) */ break; case Opt_utf8: - uopt->flags |= (1 << UDF_FLAG_UTF8); + if (!remount) { + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; + } break; case Opt_iocharset: if (!remount) { - if (uopt->nls_map) - unload_nls(uopt->nls_map); - /* - * load_nls() failure is handled later in - * udf_fill_super() after all options are - * parsed. - */ + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; + } + /* When nls_map is not loaded then UTF-8 is used */ + if (!remount && strcmp(args[0].from, "utf8") != 0) { uopt->nls_map = load_nls(args[0].from); - uopt->flags |= (1 << UDF_FLAG_NLS_MAP); + if (!uopt->nls_map) { + pr_err("iocharset %s not found\n", + args[0].from); + return 0; + } } break; case Opt_uforget: @@ -2146,21 +2151,6 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) if (!udf_parse_options((char *)options, &uopt, false)) goto parse_options_failure; - if (uopt.flags & (1 << UDF_FLAG_UTF8) && - uopt.flags & (1 << UDF_FLAG_NLS_MAP)) { - udf_err(sb, "utf8 cannot be combined with iocharset\n"); - goto parse_options_failure; - } - if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) { - uopt.nls_map = load_nls_default(); - if (!uopt.nls_map) - uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP); - else - udf_debug("Using default NLS map\n"); - } - if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP))) - uopt.flags |= (1 << UDF_FLAG_UTF8); - fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; @@ -2315,8 +2305,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) error_out: iput(sbi->s_vat_inode); parse_options_failure: - if (uopt.nls_map) - unload_nls(uopt.nls_map); + unload_nls(uopt.nls_map); if (lvid_open) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); @@ -2366,8 +2355,7 @@ static void udf_put_super(struct super_block *sb) sbi = UDF_SB(sb); iput(sbi->s_vat_inode); - if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - unload_nls(sbi->s_nls_map); + unload_nls(sbi->s_nls_map); if (!sb_rdonly(sb)) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 758efe557a19..4fa620543d30 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -20,8 +20,6 @@ #define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNHIDE 7 #define UDF_FLAG_VARCONV 8 -#define UDF_FLAG_NLS_MAP 9 -#define UDF_FLAG_UTF8 10 #define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */ #define UDF_FLAG_GID_FORGET 12 #define UDF_FLAG_UID_SET 13 diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 5fcfa96463eb..622569007b53 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -177,7 +177,7 @@ static int udf_name_from_CS0(struct super_block *sb, return 0; } - if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) + if (UDF_SB(sb)->s_nls_map) conv_f = UDF_SB(sb)->s_nls_map->uni2char; else conv_f = NULL; @@ -285,7 +285,7 @@ static int udf_name_to_CS0(struct super_block *sb, if (ocu_max_len <= 0) return 0; - if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) + if (UDF_SB(sb)->s_nls_map) conv_f = UDF_SB(sb)->s_nls_map->char2uni; else conv_f = NULL; From 28ce50f8d96ec9035f60c9348294ea26b94db944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sun, 8 Aug 2021 18:24:37 +0200 Subject: [PATCH 125/177] isofs: joliet: Fix iocharset=utf8 mount option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently iocharset=utf8 mount option is broken. To use UTF-8 as iocharset, it is required to use utf8 mount option. Fix iocharset=utf8 mount option to use be equivalent to the utf8 mount option. If UTF-8 as iocharset is used then s_nls_iocharset is set to NULL. So simplify code around, remove s_utf8 field as to distinguish between UTF-8 and non-UTF-8 it is needed just to check if s_nls_iocharset is set to NULL or not. Link: https://lore.kernel.org/r/20210808162453.1653-5-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Jan Kara --- fs/isofs/inode.c | 29 ++++++++++++++--------------- fs/isofs/isofs.h | 1 - fs/isofs/joliet.c | 4 +--- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 21edc423b79f..678e2c51b855 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -155,7 +155,6 @@ struct iso9660_options{ unsigned int overriderockperm:1; unsigned int uid_set:1; unsigned int gid_set:1; - unsigned int utf8:1; unsigned char map; unsigned char check; unsigned int blocksize; @@ -356,7 +355,6 @@ static int parse_options(char *options, struct iso9660_options *popt) popt->gid = GLOBAL_ROOT_GID; popt->uid = GLOBAL_ROOT_UID; popt->iocharset = NULL; - popt->utf8 = 0; popt->overriderockperm = 0; popt->session=-1; popt->sbsector=-1; @@ -389,10 +387,13 @@ static int parse_options(char *options, struct iso9660_options *popt) case Opt_cruft: popt->cruft = 1; break; - case Opt_utf8: - popt->utf8 = 1; - break; #ifdef CONFIG_JOLIET + case Opt_utf8: + kfree(popt->iocharset); + popt->iocharset = kstrdup("utf8", GFP_KERNEL); + if (!popt->iocharset) + return 0; + break; case Opt_iocharset: kfree(popt->iocharset); popt->iocharset = match_strdup(&args[0]); @@ -495,7 +496,6 @@ static int isofs_show_options(struct seq_file *m, struct dentry *root) if (sbi->s_nocompress) seq_puts(m, ",nocompress"); if (sbi->s_overriderockperm) seq_puts(m, ",overriderockperm"); if (sbi->s_showassoc) seq_puts(m, ",showassoc"); - if (sbi->s_utf8) seq_puts(m, ",utf8"); if (sbi->s_check) seq_printf(m, ",check=%c", sbi->s_check); if (sbi->s_mapping) seq_printf(m, ",map=%c", sbi->s_mapping); @@ -518,9 +518,10 @@ static int isofs_show_options(struct seq_file *m, struct dentry *root) seq_printf(m, ",fmode=%o", sbi->s_fmode); #ifdef CONFIG_JOLIET - if (sbi->s_nls_iocharset && - strcmp(sbi->s_nls_iocharset->charset, CONFIG_NLS_DEFAULT) != 0) + if (sbi->s_nls_iocharset) seq_printf(m, ",iocharset=%s", sbi->s_nls_iocharset->charset); + else + seq_puts(m, ",iocharset=utf8"); #endif return 0; } @@ -863,14 +864,13 @@ root_found: sbi->s_nls_iocharset = NULL; #ifdef CONFIG_JOLIET - if (joliet_level && opt.utf8 == 0) { + if (joliet_level) { char *p = opt.iocharset ? opt.iocharset : CONFIG_NLS_DEFAULT; - sbi->s_nls_iocharset = load_nls(p); - if (! sbi->s_nls_iocharset) { - /* Fail only if explicit charset specified */ - if (opt.iocharset) + if (strcmp(p, "utf8") != 0) { + sbi->s_nls_iocharset = opt.iocharset ? + load_nls(opt.iocharset) : load_nls_default(); + if (!sbi->s_nls_iocharset) goto out_freesbi; - sbi->s_nls_iocharset = load_nls_default(); } } #endif @@ -886,7 +886,6 @@ root_found: sbi->s_gid = opt.gid; sbi->s_uid_set = opt.uid_set; sbi->s_gid_set = opt.gid_set; - sbi->s_utf8 = opt.utf8; sbi->s_nocompress = opt.nocompress; sbi->s_overriderockperm = opt.overriderockperm; /* diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 055ec6c586f7..dcdc191ed183 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -44,7 +44,6 @@ struct isofs_sb_info { unsigned char s_session; unsigned int s_high_sierra:1; unsigned int s_rock:2; - unsigned int s_utf8:1; unsigned int s_cruft:1; /* Broken disks with high byte of length * containing junk */ unsigned int s_nocompress:1; diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index be8b6a9d0b92..c0f04a1e7f69 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -41,14 +41,12 @@ uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls) int get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode) { - unsigned char utf8; struct nls_table *nls; unsigned char len = 0; - utf8 = ISOFS_SB(inode->i_sb)->s_utf8; nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset; - if (utf8) { + if (!nls) { len = utf16s_to_utf8s((const wchar_t *) de->name, de->name_len[0] >> 1, UTF16_BIG_ENDIAN, outname, PAGE_SIZE); From c02aa89b7435c852aad9b2f39bdfd8ba8e22d3dc Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Wed, 11 Aug 2021 18:49:29 +0800 Subject: [PATCH 126/177] power: supply: axp288_charger: Use the defined variable to clean code Use the defined variable "dev" to make the code cleaner. Co-developed-by: Zhang Shengju Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index a4df1ea92386..b9553be9bed5 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -813,7 +813,7 @@ static int axp288_charger_probe(struct platform_device *pdev) if (val == 0) return -ENODEV; - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -823,7 +823,7 @@ static int axp288_charger_probe(struct platform_device *pdev) info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); if (info->cable.edev == NULL) { - dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n", + dev_dbg(dev, "%s is not ready, probe deferred\n", AXP288_EXTCON_DEV_NAME); return -EPROBE_DEFER; } @@ -834,8 +834,7 @@ static int axp288_charger_probe(struct platform_device *pdev) dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n"); return -EPROBE_DEFER; } - dev_info(&pdev->dev, - "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); + dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); } platform_set_drvdata(pdev, info); @@ -874,7 +873,7 @@ static int axp288_charger_probe(struct platform_device *pdev) INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; if (info->otg.cable) { - ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable, + ret = devm_extcon_register_notifier(dev, info->otg.cable, EXTCON_USB_HOST, &info->otg.id_nb); if (ret) { dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n"); @@ -899,7 +898,7 @@ static int axp288_charger_probe(struct platform_device *pdev) NULL, axp288_charger_irq_thread_handler, IRQF_ONESHOT, info->pdev->name, info); if (ret) { - dev_err(&pdev->dev, "failed to request interrupt=%d\n", + dev_err(dev, "failed to request interrupt=%d\n", info->irq[i]); return ret; } From 38334231965e9a75558e413d1f5a23357994f065 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 20 Jul 2021 09:29:22 +0100 Subject: [PATCH 127/177] power: supply: ab8500: clean up warnings found by checkpatch Clean up a handful of checkpatch warnings: - static const char * array should probably be static const char * const - function arguments should have identifier names - else should follow close brace '}' - suspect code indent for conditional statements - unnecessary parentheses in an if condition - avoid multiple line dereference - remove debug showing function execution, ftrace can trace these better - prefer 'long' over 'long int' as the int is unnecessary Signed-off-by: Colin Ian King Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_chargalg.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 4ef34f64e9c0..5583858cd00f 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -123,7 +123,7 @@ enum ab8500_chargalg_states { STATE_WD_EXPIRED, }; -static const char *states[] = { +static const char * const states[] = { "HANDHELD_INIT", "HANDHELD", "CHG_NOT_OK_INIT", @@ -274,8 +274,8 @@ static enum power_supply_property ab8500_chargalg_props[] = { struct ab8500_chargalg_sysfs_entry { struct attribute attr; - ssize_t (*show)(struct ab8500_chargalg *, char *); - ssize_t (*store)(struct ab8500_chargalg *, const char *, size_t); + ssize_t (*show)(struct ab8500_chargalg *di, char *buf); + ssize_t (*store)(struct ab8500_chargalg *di, const char *buf, size_t length); }; /** @@ -526,8 +526,7 @@ static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) di->usb_chg->ops.kick_wd(di->usb_chg); return di->ac_chg->ops.kick_wd(di->ac_chg); - } - else if (di->usb_chg && di->usb_chg->ops.kick_wd && + } else if (di->usb_chg && di->usb_chg->ops.kick_wd && di->chg_info.online_chg & USB_CHG) return di->usb_chg->ops.kick_wd(di->usb_chg); @@ -750,8 +749,8 @@ static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di) di->t_hyst_norm = 0; di->t_hyst_lowhigh = di->bm->temp_hysteresis; } else { - /* Within hysteresis */ - dev_dbg(di->dev, "Within hysteresis limit temp: %d " + /* Within hysteresis */ + dev_dbg(di->dev, "Within hysteresis limit temp: %d " "hyst_lowhigh %d, hyst normal %d\n", di->batt_data.temp, di->t_hyst_lowhigh, di->t_hyst_norm); @@ -867,7 +866,7 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di) di->ccm.wait_cnt = 0; - if ((di->batt_data.inst_curr > di->ccm.original_iset)) { + if (di->batt_data.inst_curr > di->ccm.original_iset) { dev_dbg(di->dev, " Maximization Ibat (%dmA) too high" " (limit %dmA) (current iset: %dmA)!\n", di->batt_data.inst_curr, di->ccm.original_iset, @@ -1544,8 +1543,7 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) case STATE_WAIT_FOR_RECHARGE: if (di->batt_data.percent <= - di->bm->bat_type[di->bm->batt_id]. - recharge_cap) + di->bm->bat_type[di->bm->batt_id].recharge_cap) ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; @@ -1675,8 +1673,6 @@ static void ab8500_chargalg_wd_work(struct work_struct *work) struct ab8500_chargalg *di = container_of(work, struct ab8500_chargalg, chargalg_wd_work.work); - dev_dbg(di->dev, "ab8500_chargalg_wd_work\n"); - ret = ab8500_chargalg_kick_watchdog(di); if (ret < 0) dev_err(di->dev, "failed to kick watchdog\n"); @@ -1753,7 +1749,7 @@ static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di, static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di, const char *buf, size_t length) { - long int param; + long param; int ret; ret = kstrtol(buf, 10, ¶m); @@ -1786,7 +1782,7 @@ static ssize_t ab8500_chargalg_en_show(struct ab8500_chargalg *di, static ssize_t ab8500_chargalg_en_store(struct ab8500_chargalg *di, const char *buf, size_t length) { - long int param; + long param; int ac_usb; int ret; From e12ef7bf34113f55c9bf444a680a15b6daf76f26 Mon Sep 17 00:00:00 2001 From: Gene Chen Date: Mon, 19 Jul 2021 11:39:12 +0800 Subject: [PATCH 128/177] lib: add linear range get selector within Add linear range get selector within for choose closest selector between minimum and maximum selector. Signed-off-by: Gene Chen Reviewed-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- include/linux/linear_range.h | 2 ++ lib/linear_ranges.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/linux/linear_range.h b/include/linux/linear_range.h index 17b5943727d5..fd3d0b358f22 100644 --- a/include/linux/linear_range.h +++ b/include/linux/linear_range.h @@ -41,6 +41,8 @@ int linear_range_get_selector_low(const struct linear_range *r, int linear_range_get_selector_high(const struct linear_range *r, unsigned int val, unsigned int *selector, bool *found); +void linear_range_get_selector_within(const struct linear_range *r, + unsigned int val, unsigned int *selector); int linear_range_get_selector_low_array(const struct linear_range *r, int ranges, unsigned int val, unsigned int *selector, bool *found); diff --git a/lib/linear_ranges.c b/lib/linear_ranges.c index ced5c15d3f04..a1a7dfa881de 100644 --- a/lib/linear_ranges.c +++ b/lib/linear_ranges.c @@ -241,5 +241,36 @@ int linear_range_get_selector_high(const struct linear_range *r, } EXPORT_SYMBOL_GPL(linear_range_get_selector_high); +/** + * linear_range_get_selector_within - return linear range selector for value + * @r: pointer to linear range where selector is looked from + * @val: value for which the selector is searched + * @selector: address where found selector value is updated + * + * Return selector for which range value is closest match for given + * input value. Value is matching if it is equal or lower than given + * value. But return maximum selector if given value is higher than + * maximum value. + */ +void linear_range_get_selector_within(const struct linear_range *r, + unsigned int val, unsigned int *selector) +{ + if (r->min > val) { + *selector = r->min_sel; + return; + } + + if (linear_range_get_max_value(r) < val) { + *selector = r->max_sel; + return; + } + + if (r->step == 0) + *selector = r->min_sel; + else + *selector = (val - r->min) / r->step + r->min_sel; +} +EXPORT_SYMBOL_GPL(linear_range_get_selector_within); + MODULE_DESCRIPTION("linear-ranges helper"); MODULE_LICENSE("GPL"); From 23531eec79b659d12f28a6088f0b1ea94975a93c Mon Sep 17 00:00:00 2001 From: Gene Chen Date: Mon, 19 Jul 2021 11:39:13 +0800 Subject: [PATCH 129/177] dt-bindings: power: Add bindings document for Charger support on MT6360 PMIC Add bindings document for Charger support on MT6360 PMIC Signed-off-by: Gene Chen Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/mt6360_charger.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml diff --git a/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml b/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml new file mode 100644 index 000000000000..b89b15a5bfa4 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/mt6360_charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Battery charger driver for MT6360 PMIC from MediaTek Integrated. + +maintainers: + - Gene Chen + +description: | + This module is part of the MT6360 MFD device. + Provides Battery Charger, Boost for OTG devices and BC1.2 detection. + +properties: + compatible: + const: mediatek,mt6360-chg + + richtek,vinovp-microvolt: + description: Maximum CHGIN regulation voltage in uV. + enum: [ 5500000, 6500000, 11000000, 14500000 ] + + + usb-otg-vbus-regulator: + type: object + description: OTG boost regulator. + $ref: /schemas/regulator/regulator.yaml# + +required: + - compatible + +additionalProperties: false + +examples: + - | + mt6360_charger: charger { + compatible = "mediatek,mt6360-chg"; + richtek,vinovp-microvolt = <14500000>; + + otg_vbus_regulator: usb-otg-vbus-regulator { + regulator-compatible = "usb-otg-vbus"; + regulator-name = "usb-otg-vbus"; + regulator-min-microvolt = <4425000>; + regulator-max-microvolt = <5825000>; + }; + }; +... From 0402e8ebb8b869e375e8af7243044df21b5ff378 Mon Sep 17 00:00:00 2001 From: Gene Chen Date: Mon, 19 Jul 2021 11:39:14 +0800 Subject: [PATCH 130/177] power: supply: mt6360_charger: add MT6360 charger support Add basic support for the battery charger for MT6360 PMIC Signed-off-by: Gene Chen Reviewed-by: Matti Vaittinen Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/mt6360_charger.c | 867 ++++++++++++++++++++++++++ 3 files changed, 879 insertions(+) create mode 100644 drivers/power/supply/mt6360_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 11f5368e810e..06b16e4a7915 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -577,6 +577,17 @@ config CHARGER_MP2629 Battery charger. This driver provides Battery charger power management functions on the systems. +config CHARGER_MT6360 + tristate "Mediatek MT6360 Charger Driver" + depends on MFD_MT6360 + depends on REGULATOR + select LINEAR_RANGES + help + Say Y here to enable MT6360 Charger Part. + The device supports High-Accuracy Voltage/Current Regulation, + Average Input Current Regulation, Battery Temperature Sensing, + Over-Temperature Protection, DPDM Detection for BC1.2. + config CHARGER_QCOM_SMBB tristate "Qualcomm Switch-Mode Battery Charger and Boost" depends on MFD_SPMI_PMIC || COMPILE_TEST diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 33059a91f60c..ebf3f6648ff4 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o +obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o diff --git a/drivers/power/supply/mt6360_charger.c b/drivers/power/supply/mt6360_charger.c new file mode 100644 index 000000000000..3abaa72e0668 --- /dev/null +++ b/drivers/power/supply/mt6360_charger.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MT6360_PMU_CHG_CTRL1 0x311 +#define MT6360_PMU_CHG_CTRL2 0x312 +#define MT6360_PMU_CHG_CTRL3 0x313 +#define MT6360_PMU_CHG_CTRL4 0x314 +#define MT6360_PMU_CHG_CTRL5 0x315 +#define MT6360_PMU_CHG_CTRL6 0x316 +#define MT6360_PMU_CHG_CTRL7 0x317 +#define MT6360_PMU_CHG_CTRL8 0x318 +#define MT6360_PMU_CHG_CTRL9 0x319 +#define MT6360_PMU_CHG_CTRL10 0x31A +#define MT6360_PMU_DEVICE_TYPE 0x322 +#define MT6360_PMU_USB_STATUS1 0x327 +#define MT6360_PMU_CHG_STAT 0x34A +#define MT6360_PMU_CHG_CTRL19 0x361 +#define MT6360_PMU_FOD_STAT 0x3E7 + +/* MT6360_PMU_CHG_CTRL1 */ +#define MT6360_FSLP_SHFT (3) +#define MT6360_FSLP_MASK BIT(MT6360_FSLP_SHFT) +#define MT6360_OPA_MODE_SHFT (0) +#define MT6360_OPA_MODE_MASK BIT(MT6360_OPA_MODE_SHFT) +/* MT6360_PMU_CHG_CTRL2 */ +#define MT6360_IINLMTSEL_SHFT (2) +#define MT6360_IINLMTSEL_MASK GENMASK(3, 2) +/* MT6360_PMU_CHG_CTRL3 */ +#define MT6360_IAICR_SHFT (2) +#define MT6360_IAICR_MASK GENMASK(7, 2) +#define MT6360_ILIM_EN_MASK BIT(0) +/* MT6360_PMU_CHG_CTRL4 */ +#define MT6360_VOREG_SHFT (1) +#define MT6360_VOREG_MASK GENMASK(7, 1) +/* MT6360_PMU_CHG_CTRL5 */ +#define MT6360_VOBST_MASK GENMASK(7, 2) +/* MT6360_PMU_CHG_CTRL6 */ +#define MT6360_VMIVR_SHFT (1) +#define MT6360_VMIVR_MASK GENMASK(7, 1) +/* MT6360_PMU_CHG_CTRL7 */ +#define MT6360_ICHG_SHFT (2) +#define MT6360_ICHG_MASK GENMASK(7, 2) +/* MT6360_PMU_CHG_CTRL8 */ +#define MT6360_IPREC_SHFT (0) +#define MT6360_IPREC_MASK GENMASK(3, 0) +/* MT6360_PMU_CHG_CTRL9 */ +#define MT6360_IEOC_SHFT (4) +#define MT6360_IEOC_MASK GENMASK(7, 4) +/* MT6360_PMU_CHG_CTRL10 */ +#define MT6360_OTG_OC_MASK GENMASK(3, 0) +/* MT6360_PMU_DEVICE_TYPE */ +#define MT6360_USBCHGEN_MASK BIT(7) +/* MT6360_PMU_USB_STATUS1 */ +#define MT6360_USB_STATUS_SHFT (4) +#define MT6360_USB_STATUS_MASK GENMASK(6, 4) +/* MT6360_PMU_CHG_STAT */ +#define MT6360_CHG_STAT_SHFT (6) +#define MT6360_CHG_STAT_MASK GENMASK(7, 6) +#define MT6360_VBAT_LVL_MASK BIT(5) +/* MT6360_PMU_CHG_CTRL19 */ +#define MT6360_VINOVP_SHFT (5) +#define MT6360_VINOVP_MASK GENMASK(6, 5) +/* MT6360_PMU_FOD_STAT */ +#define MT6360_CHRDET_EXT_MASK BIT(4) + +/* uV */ +#define MT6360_VMIVR_MIN 3900000 +#define MT6360_VMIVR_MAX 13400000 +#define MT6360_VMIVR_STEP 100000 +/* uA */ +#define MT6360_ICHG_MIN 100000 +#define MT6360_ICHG_MAX 5000000 +#define MT6360_ICHG_STEP 100000 +/* uV */ +#define MT6360_VOREG_MIN 3900000 +#define MT6360_VOREG_MAX 4710000 +#define MT6360_VOREG_STEP 10000 +/* uA */ +#define MT6360_AICR_MIN 100000 +#define MT6360_AICR_MAX 3250000 +#define MT6360_AICR_STEP 50000 +/* uA */ +#define MT6360_IPREC_MIN 100000 +#define MT6360_IPREC_MAX 850000 +#define MT6360_IPREC_STEP 50000 +/* uA */ +#define MT6360_IEOC_MIN 100000 +#define MT6360_IEOC_MAX 850000 +#define MT6360_IEOC_STEP 50000 + +enum { + MT6360_RANGE_VMIVR, + MT6360_RANGE_ICHG, + MT6360_RANGE_VOREG, + MT6360_RANGE_AICR, + MT6360_RANGE_IPREC, + MT6360_RANGE_IEOC, + MT6360_RANGE_MAX, +}; + +#define MT6360_LINEAR_RANGE(idx, _min, _min_sel, _max_sel, _step) \ + [idx] = REGULATOR_LINEAR_RANGE(_min, _min_sel, _max_sel, _step) + +static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = { + MT6360_LINEAR_RANGE(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000), + MT6360_LINEAR_RANGE(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000), + MT6360_LINEAR_RANGE(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000), + MT6360_LINEAR_RANGE(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000), + MT6360_LINEAR_RANGE(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000), + MT6360_LINEAR_RANGE(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000), +}; + +struct mt6360_chg_info { + struct device *dev; + struct regmap *regmap; + struct power_supply_desc psy_desc; + struct power_supply *psy; + struct regulator_dev *otg_rdev; + struct mutex chgdet_lock; + u32 vinovp; + bool pwr_rdy; + bool bc12_en; + int psy_usb_type; + struct work_struct chrdet_work; +}; + +enum mt6360_iinlmtsel { + MT6360_IINLMTSEL_AICR_3250 = 0, + MT6360_IINLMTSEL_CHG_TYPE, + MT6360_IINLMTSEL_AICR, + MT6360_IINLMTSEL_LOWER_LEVEL, +}; + +enum mt6360_pmu_chg_type { + MT6360_CHG_TYPE_NOVBUS = 0, + MT6360_CHG_TYPE_UNDER_GOING, + MT6360_CHG_TYPE_SDP, + MT6360_CHG_TYPE_SDPNSTD, + MT6360_CHG_TYPE_DCP, + MT6360_CHG_TYPE_CDP, + MT6360_CHG_TYPE_DISABLE_BC12, + MT6360_CHG_TYPE_MAX, +}; + +static enum power_supply_usb_type mt6360_charger_usb_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci, + bool *pwr_rdy) +{ + int ret; + unsigned int regval; + + ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, ®val); + if (ret < 0) + return ret; + *pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false; + return 0; +} + +static int mt6360_charger_get_online(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + bool pwr_rdy; + + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); + if (ret < 0) + return ret; + val->intval = pwr_rdy ? true : false; + return 0; +} + +static int mt6360_charger_get_status(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int status, ret; + unsigned int regval; + bool pwr_rdy; + + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); + if (ret < 0) + return ret; + if (!pwr_rdy) { + status = POWER_SUPPLY_STATUS_DISCHARGING; + goto out; + } + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, ®val); + if (ret < 0) + return ret; + regval &= MT6360_CHG_STAT_MASK; + regval >>= MT6360_CHG_STAT_SHFT; + switch (regval) { + case 0x0: + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case 0x1: + status = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0x2: + status = POWER_SUPPLY_STATUS_FULL; + break; + default: + ret = -EIO; + } +out: + if (!ret) + val->intval = status; + return ret; +} + +static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int type, ret; + unsigned int regval; + u8 chg_stat; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, ®val); + if (ret < 0) + return ret; + + chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT; + switch (chg_stat) { + case 0x01: /* Charge in Progress */ + if (regval & MT6360_VBAT_LVL_MASK) + type = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case 0x00: /* Not Charging */ + case 0x02: /* Charge Done */ + case 0x03: /* Charge Fault */ + default: + type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + val->intval = type; + return 0; +} + +static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + val->intval = MT6360_ICHG_MAX; + return 0; +} + +static int mt6360_charger_get_cv(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + val->intval = MT6360_VOREG_MAX; + return 0; +} + +static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci, + union power_supply_propval *val) +{ + int ret; + u32 sel, value; + + ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel); + if (ret < 0) + return ret; + sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT; + ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value); + if (!ret) + val->intval = value; + return ret; +} + +static int mt6360_charger_set_online(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u8 force_sleep = val->intval ? 0 : 1; + + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL1, + MT6360_FSLP_MASK, + force_sleep << MT6360_FSLP_SHFT); +} + +static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL7, + MT6360_ICHG_MASK, + sel << MT6360_ICHG_SHFT); +} + +static int mt6360_charger_set_cv(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL4, + MT6360_VOREG_MASK, + sel << MT6360_VOREG_SHFT); +} + +static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL3, + MT6360_IAICR_MASK, + sel << MT6360_IAICR_SHFT); +} + +static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL3, + MT6360_VMIVR_MASK, + sel << MT6360_VMIVR_SHFT); +} + +static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL8, + MT6360_IPREC_MASK, + sel << MT6360_IPREC_SHFT); +} + +static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci, + const union power_supply_propval *val) +{ + u32 sel; + + linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel); + return regmap_update_bits(mci->regmap, + MT6360_PMU_CHG_CTRL9, + MT6360_IEOC_MASK, + sel << MT6360_IEOC_SHFT); +} + +static int mt6360_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mt6360_chg_info *mci = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = mt6360_charger_get_online(mci, val); + break; + case POWER_SUPPLY_PROP_STATUS: + ret = mt6360_charger_get_status(mci, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = mt6360_charger_get_charge_type(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = mt6360_charger_get_ichg(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = mt6360_charger_get_max_ichg(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = mt6360_charger_get_cv(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = mt6360_charger_get_max_cv(mci, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = mt6360_charger_get_aicr(mci, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = mt6360_charger_get_mivr(mci, val); + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = mt6360_charger_get_iprechg(mci, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = mt6360_charger_get_ieoc(mci, val); + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = mci->psy_usb_type; + break; + default: + ret = -ENODATA; + } + return ret; +} + +static int mt6360_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mt6360_chg_info *mci = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = mt6360_charger_set_online(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = mt6360_charger_set_ichg(mci, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = mt6360_charger_set_cv(mci, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = mt6360_charger_set_aicr(mci, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = mt6360_charger_set_mivr(mci, val); + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = mt6360_charger_set_iprechg(mci, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = mt6360_charger_set_ieoc(mci, val); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int mt6360_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return 1; + default: + return 0; + } +} + +static enum power_supply_property mt6360_charger_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_USB_TYPE, +}; + +static const struct power_supply_desc mt6360_charger_desc = { + .type = POWER_SUPPLY_TYPE_USB, + .properties = mt6360_charger_properties, + .num_properties = ARRAY_SIZE(mt6360_charger_properties), + .get_property = mt6360_charger_get_property, + .set_property = mt6360_charger_set_property, + .property_is_writeable = mt6360_charger_property_is_writeable, + .usb_types = mt6360_charger_usb_types, + .num_usb_types = ARRAY_SIZE(mt6360_charger_usb_types), +}; + +static const struct regulator_ops mt6360_chg_otg_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_desc mt6360_otg_rdesc = { + .of_match = "usb-otg-vbus", + .name = "usb-otg-vbus", + .ops = &mt6360_chg_otg_ops, + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .min_uV = 4425000, + .uV_step = 25000, + .n_voltages = 57, + .vsel_reg = MT6360_PMU_CHG_CTRL5, + .vsel_mask = MT6360_VOBST_MASK, + .enable_reg = MT6360_PMU_CHG_CTRL1, + .enable_mask = MT6360_OPA_MODE_MASK, +}; + +static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data) +{ + struct mt6360_chg_info *mci = data; + int ret; + unsigned int usb_status; + int last_usb_type; + + mutex_lock(&mci->chgdet_lock); + if (!mci->bc12_en) { + dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n"); + goto out; + } + last_usb_type = mci->psy_usb_type; + /* Plug in */ + ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status); + if (ret < 0) + goto out; + usb_status &= MT6360_USB_STATUS_MASK; + usb_status >>= MT6360_USB_STATUS_SHFT; + switch (usb_status) { + case MT6360_CHG_TYPE_NOVBUS: + dev_dbg(mci->dev, "Received attach interrupt, no vbus\n"); + goto out; + case MT6360_CHG_TYPE_UNDER_GOING: + dev_dbg(mci->dev, "Received attach interrupt, under going...\n"); + goto out; + case MT6360_CHG_TYPE_SDP: + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + case MT6360_CHG_TYPE_SDPNSTD: + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + case MT6360_CHG_TYPE_CDP: + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + case MT6360_CHG_TYPE_DCP: + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + case MT6360_CHG_TYPE_DISABLE_BC12: + dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n"); + goto out; + default: + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + dev_dbg(mci->dev, "Received attach interrupt, reserved address\n"); + goto out; + } + + dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type); + if (last_usb_type != mci->psy_usb_type) + power_supply_changed(mci->psy); +out: + mutex_unlock(&mci->chgdet_lock); + return IRQ_HANDLED; +} + +static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci) +{ + int ret; + bool pwr_rdy; + + mutex_lock(&mci->chgdet_lock); + ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy); + if (ret < 0) + goto out; + if (mci->pwr_rdy == pwr_rdy) { + dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy); + goto out; + } + mci->pwr_rdy = pwr_rdy; + dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy); + if (!pwr_rdy) { + mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + power_supply_changed(mci->psy); + + } + ret = regmap_update_bits(mci->regmap, + MT6360_PMU_DEVICE_TYPE, + MT6360_USBCHGEN_MASK, + pwr_rdy ? MT6360_USBCHGEN_MASK : 0); + if (ret < 0) + goto out; + mci->bc12_en = pwr_rdy; +out: + mutex_unlock(&mci->chgdet_lock); +} + +static void mt6360_chrdet_work(struct work_struct *work) +{ + struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of( + work, struct mt6360_chg_info, chrdet_work); + + mt6360_handle_chrdet_ext_evt(mci); +} + +static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data) +{ + struct mt6360_chg_info *mci = data; + + mt6360_handle_chrdet_ext_evt(mci); + return IRQ_HANDLED; +} + +static int mt6360_chg_irq_register(struct platform_device *pdev) +{ + const struct { + const char *name; + irq_handler_t handler; + } irq_descs[] = { + { "attach_i", mt6360_pmu_attach_i_handler }, + { "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler } + }; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(irq_descs); i++) { + ret = platform_get_irq_byname(pdev, irq_descs[i].name); + if (ret < 0) + return ret; + + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, + irq_descs[i].handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + irq_descs[i].name, + platform_get_drvdata(pdev)); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n", + irq_descs[i].name); + } + + return 0; +} + +static u32 mt6360_vinovp_trans_to_sel(u32 val) +{ + u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 }; + int i; + + /* Select the smaller and equal supported value */ + for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) { + if (val < vinovp_tbl[i+1]) + break; + } + return i; +} + +static int mt6360_chg_init_setting(struct mt6360_chg_info *mci) +{ + int ret; + u32 sel; + + sel = mt6360_vinovp_trans_to_sel(mci->vinovp); + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19, + MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT); + if (ret) + return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__); + ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE, + MT6360_USBCHGEN_MASK, 0); + if (ret) + return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__); + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2, + MT6360_IINLMTSEL_MASK, + MT6360_IINLMTSEL_AICR << + MT6360_IINLMTSEL_SHFT); + if (ret) + return dev_err_probe(mci->dev, ret, + "%s: Failed to switch iinlmtsel to aicr\n", __func__); + usleep_range(5000, 6000); + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3, + MT6360_ILIM_EN_MASK, 0); + if (ret) + return dev_err_probe(mci->dev, ret, + "%s: Failed to disable ilim\n", __func__); + ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10, + MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK); + if (ret) + return dev_err_probe(mci->dev, ret, + "%s: Failed to config otg oc to 3A\n", __func__); + return 0; +} + +static int mt6360_charger_probe(struct platform_device *pdev) +{ + struct mt6360_chg_info *mci; + struct power_supply_config charger_cfg = {}; + struct regulator_config config = { }; + int ret; + + mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL); + if (!mci) + return -ENOMEM; + + mci->dev = &pdev->dev; + mci->vinovp = 6500000; + mutex_init(&mci->chgdet_lock); + platform_set_drvdata(pdev, mci); + devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work); + + ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp); + if (ret) + dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n"); + + mci->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!mci->regmap) + return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n"); + + ret = mt6360_chg_init_setting(mci); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n"); + + memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc)); + mci->psy_desc.name = dev_name(&pdev->dev); + charger_cfg.drv_data = mci; + charger_cfg.of_node = pdev->dev.of_node; + mci->psy = devm_power_supply_register(&pdev->dev, + &mci->psy_desc, &charger_cfg); + if (IS_ERR(mci->psy)) + return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy), + "Failed to register power supply dev\n"); + + + ret = mt6360_chg_irq_register(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n"); + + config.dev = &pdev->dev; + config.regmap = mci->regmap; + mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc, + &config); + if (IS_ERR(mci->otg_rdev)) + return PTR_ERR(mci->otg_rdev); + + schedule_work(&mci->chrdet_work); + + return 0; +} + +static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = { + { .compatible = "mediatek,mt6360-chg", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6360_charger_of_id); + +static const struct platform_device_id mt6360_charger_id[] = { + { "mt6360-chg", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, mt6360_charger_id); + +static struct platform_driver mt6360_charger_driver = { + .driver = { + .name = "mt6360-chg", + .of_match_table = of_match_ptr(mt6360_charger_of_id), + }, + .probe = mt6360_charger_probe, + .id_table = mt6360_charger_id, +}; +module_platform_driver(mt6360_charger_driver); + +MODULE_AUTHOR("Gene Chen "); +MODULE_DESCRIPTION("MT6360 Charger Driver"); +MODULE_LICENSE("GPL"); From 1a844ddf06b0a6f39c9d8974dfecfda347e87cb6 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 12 Jul 2021 23:21:10 +0200 Subject: [PATCH 131/177] iio: adc: rn5t618: Add iio map Add iio map to allow power driver to read out values as a consumer. This approach does not block later addition of devicetree support which would be helpful if there is an in-kernel consumer for AIN0/1. Signed-off-by: Andreas Kemnade Acked-by: Jonathan Cameron Signed-off-by: Sebastian Reichel --- drivers/iio/adc/rn5t618-adc.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index 7010c4276947..c56fccb2c8e1 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500)) @@ -189,6 +191,19 @@ static const struct iio_chan_spec rn5t618_adc_iio_channels[] = { RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0") }; +static struct iio_map rn5t618_maps[] = { + IIO_MAP("VADP", "rn5t618-power", "vadp"), + IIO_MAP("VUSB", "rn5t618-power", "vusb"), + { /* sentinel */ } +}; + +static void unregister_map(void *data) +{ + struct iio_dev *iio_dev = (struct iio_dev *) data; + + iio_map_array_unregister(iio_dev); +} + static int rn5t618_adc_probe(struct platform_device *pdev) { int ret; @@ -239,6 +254,14 @@ static int rn5t618_adc_probe(struct platform_device *pdev) return ret; } + ret = iio_map_array_register(iio_dev, rn5t618_maps); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(adc->dev, unregister_map, iio_dev); + if (ret < 0) + return ret; + return devm_iio_device_register(adc->dev, iio_dev); } From 2f5caa26a074854273194207a40b7ee81e51712d Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 12 Jul 2021 23:21:11 +0200 Subject: [PATCH 132/177] power: supply: rn5t618: Add voltage_now property Read voltage_now via IIO and provide the property. Signed-off-by: Andreas Kemnade Reported-by: kernel test robot # missing depends on IIO Acked-by: Jonathan Cameron Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 2 ++ drivers/power/supply/rn5t618_power.c | 38 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 331e1160e411..4c5fc57070e4 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -792,6 +792,8 @@ config CHARGER_WILCO config RN5T618_POWER tristate "RN5T618 charger/fuel gauge support" depends on MFD_RN5T618 + depends on RN5T618_ADC + depends on IIO help Say Y here to have support for RN5T618 PMIC family fuel gauge and charger. This driver can also be built as a module. If so, the module will be diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 819061918b2a..a5e09ac78a50 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -64,6 +66,8 @@ struct rn5t618_power_info { struct power_supply *battery; struct power_supply *usb; struct power_supply *adp; + struct iio_channel *channel_vusb; + struct iio_channel *channel_vadp; int irq; }; @@ -77,6 +81,7 @@ static enum power_supply_usb_type rn5t618_usb_types[] = { static enum power_supply_property rn5t618_usb_props[] = { /* input current limit is not very accurate */ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_ONLINE, @@ -85,6 +90,7 @@ static enum power_supply_property rn5t618_usb_props[] = { static enum power_supply_property rn5t618_adp_props[] = { /* input current limit is not very accurate */ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, }; @@ -463,6 +469,15 @@ static int rn5t618_adp_get_property(struct power_supply *psy, return ret; val->intval = FROM_CUR_REG(regval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (!info->channel_vadp) + return -ENODATA; + + ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000); + if (ret < 0) + return ret; + break; default: return -EINVAL; @@ -588,6 +603,15 @@ static int rn5t618_usb_get_property(struct power_supply *psy, val->intval = FROM_CUR_REG(regval); } + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (!info->channel_vusb) + return -ENODATA; + + ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000); + if (ret < 0) + return ret; + break; default: return -EINVAL; @@ -711,6 +735,20 @@ static int rn5t618_power_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); + info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb"); + if (IS_ERR(info->channel_vusb)) { + if (PTR_ERR(info->channel_vusb) == -ENODEV) + return -EPROBE_DEFER; + return PTR_ERR(info->channel_vusb); + } + + info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp"); + if (IS_ERR(info->channel_vadp)) { + if (PTR_ERR(info->channel_vadp) == -ENODEV) + return -EPROBE_DEFER; + return PTR_ERR(info->channel_vadp); + } + ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v); if (ret) return ret; From 27a8ff4648f5f733026f43d991f651d5724bfa90 Mon Sep 17 00:00:00 2001 From: Bruno Meneguele Date: Fri, 9 Jul 2021 11:27:30 -0300 Subject: [PATCH 133/177] power: supply: bq24735: reorganize ChargeOption command macros Rename ChargeOption macros to match the others for ChargeCurrent and ChargeVoltage and also separate the command & masks macros from the bits of interest macros for each command. This macro doesn't introduce any functional change, only code re-org. Signed-off-by: Bruno Meneguele Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24735-charger.c | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index b5d619db79f6..3ce36d09c017 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -31,9 +31,8 @@ #include -#define BQ24735_CHG_OPT 0x12 -#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0) -#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4) +/* BQ24735 available commands and their respective masks */ +#define BQ24735_CHARGE_OPT 0x12 #define BQ24735_CHARGE_CURRENT 0x14 #define BQ24735_CHARGE_CURRENT_MASK 0x1fc0 #define BQ24735_CHARGE_VOLTAGE 0x15 @@ -43,6 +42,10 @@ #define BQ24735_MANUFACTURER_ID 0xfe #define BQ24735_DEVICE_ID 0xff +/* ChargeOptions bits of interest */ +#define BQ24735_CHARGE_OPT_CHG_DISABLE (1 << 0) +#define BQ24735_CHARGE_OPT_AC_PRESENT (1 << 4) + struct bq24735 { struct power_supply *charger; struct power_supply_desc charger_desc; @@ -167,8 +170,8 @@ static inline int bq24735_enable_charging(struct bq24735 *charger) if (ret) return ret; - return bq24735_update_word(charger->client, BQ24735_CHG_OPT, - BQ24735_CHG_OPT_CHARGE_DISABLE, 0); + return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT, + BQ24735_CHARGE_OPT_CHG_DISABLE, 0); } static inline int bq24735_disable_charging(struct bq24735 *charger) @@ -176,9 +179,9 @@ static inline int bq24735_disable_charging(struct bq24735 *charger) if (charger->pdata->ext_control) return 0; - return bq24735_update_word(charger->client, BQ24735_CHG_OPT, - BQ24735_CHG_OPT_CHARGE_DISABLE, - BQ24735_CHG_OPT_CHARGE_DISABLE); + return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT, + BQ24735_CHARGE_OPT_CHG_DISABLE, + BQ24735_CHARGE_OPT_CHG_DISABLE); } static bool bq24735_charger_is_present(struct bq24735 *charger) @@ -188,14 +191,14 @@ static bool bq24735_charger_is_present(struct bq24735 *charger) } else { int ac = 0; - ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT); + ac = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT); if (ac < 0) { dev_dbg(&charger->client->dev, "Failed to read charger options : %d\n", ac); return false; } - return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false; + return (ac & BQ24735_CHARGE_OPT_AC_PRESENT) ? true : false; } return false; @@ -208,11 +211,11 @@ static int bq24735_charger_is_charging(struct bq24735 *charger) if (!bq24735_charger_is_present(charger)) return 0; - ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT); + ret = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT); if (ret < 0) return ret; - return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE); + return !(ret & BQ24735_CHARGE_OPT_CHG_DISABLE); } static void bq24735_update(struct bq24735 *charger) From e2f471efe1d607a7aff38ce53ec717cebe4283d6 Mon Sep 17 00:00:00 2001 From: Pawel Dembicki Date: Thu, 24 Jun 2021 11:18:11 +0200 Subject: [PATCH 134/177] power: reset: linkstation-poweroff: prepare for new devices This commit prepare driver for another device support. New power_off_cfg structure describes two most important things: name of mdio bus and pointer to register setting function. It allow to add new device with different mdio bus node and other phy register config. Signed-off-by: Pawel Dembicki Signed-off-by: Sebastian Reichel --- drivers/power/reset/linkstation-poweroff.c | 37 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c index f1e843df0e16..cb5a32f852c1 100644 --- a/drivers/power/reset/linkstation-poweroff.c +++ b/drivers/power/reset/linkstation-poweroff.c @@ -29,11 +29,21 @@ #define LED2_FORCE_ON (0x8 << 8) #define LEDMASK GENMASK(11,8) -static struct phy_device *phydev; +struct power_off_cfg { + char *mdio_node_name; + void (*phy_set_reg)(bool restart); +}; -static void mvphy_reg_intn(u16 data) +static struct phy_device *phydev; +static const struct power_off_cfg *cfg; + +static void linkstation_mvphy_reg_intn(bool restart) { int rc = 0, saved_page; + u16 data = 0; + + if (restart) + data = MII_88E1318S_PHY_LED_TCR_FORCE_INT; saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE); if (saved_page < 0) @@ -66,11 +76,16 @@ err: dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc); } +static const struct power_off_cfg linkstation_power_off_cfg = { + .mdio_node_name = "mdio", + .phy_set_reg = linkstation_mvphy_reg_intn, +}; + static int linkstation_reboot_notifier(struct notifier_block *nb, unsigned long action, void *unused) { if (action == SYS_RESTART) - mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT); + cfg->phy_set_reg(true); return NOTIFY_DONE; } @@ -82,14 +97,18 @@ static struct notifier_block linkstation_reboot_nb = { static void linkstation_poweroff(void) { unregister_reboot_notifier(&linkstation_reboot_nb); - mvphy_reg_intn(0); + cfg->phy_set_reg(false); kernel_restart("Power off"); } static const struct of_device_id ls_poweroff_of_match[] = { - { .compatible = "buffalo,ls421d" }, - { .compatible = "buffalo,ls421de" }, + { .compatible = "buffalo,ls421d", + .data = &linkstation_power_off_cfg, + }, + { .compatible = "buffalo,ls421de", + .data = &linkstation_power_off_cfg, + }, { }, }; @@ -97,13 +116,17 @@ static int __init linkstation_poweroff_init(void) { struct mii_bus *bus; struct device_node *dn; + const struct of_device_id *match; dn = of_find_matching_node(NULL, ls_poweroff_of_match); if (!dn) return -ENODEV; of_node_put(dn); - dn = of_find_node_by_name(NULL, "mdio"); + match = of_match_node(ls_poweroff_of_match, dn); + cfg = match->data; + + dn = of_find_node_by_name(NULL, cfg->mdio_node_name); if (!dn) return -ENODEV; From 0c77ec3da8c156d6d02ce0934b590cfe8a313cae Mon Sep 17 00:00:00 2001 From: Pawel Dembicki Date: Thu, 24 Jun 2021 11:18:12 +0200 Subject: [PATCH 135/177] power: reset: linkstation-poweroff: add new device This commit introduces support for NETGEAR ReadyNAS Duo v2. This device use bit 4 of LED[2:0] Polarity Control Register to indicate AC Power loss. For more details about AC loss detection in NETGEAR ReadyNAS Duo v2, please look at the file: RND_5.3.13_WW.src/u-boot/board/mv_feroceon/mv_hal/usibootup/usibootup.c from Netgear GPL sources. Signed-off-by: Pawel Dembicki Signed-off-by: Sebastian Reichel --- drivers/power/reset/linkstation-poweroff.c | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c index cb5a32f852c1..02f5fdb8ffc4 100644 --- a/drivers/power/reset/linkstation-poweroff.c +++ b/drivers/power/reset/linkstation-poweroff.c @@ -19,6 +19,7 @@ #define MII_MARVELL_PHY_PAGE 22 #define MII_PHY_LED_CTRL 16 +#define MII_PHY_LED_POL_CTRL 17 #define MII_88E1318S_PHY_LED_TCR 18 #define MII_88E1318S_PHY_WOL_CTRL 16 #define MII_M1011_IEVENT 19 @@ -29,6 +30,8 @@ #define LED2_FORCE_ON (0x8 << 8) #define LEDMASK GENMASK(11,8) +#define MII_88E1318S_PHY_LED_POL_LED2 BIT(4) + struct power_off_cfg { char *mdio_node_name; void (*phy_set_reg)(bool restart); @@ -76,11 +79,47 @@ err: dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc); } +static void readynas_mvphy_set_reg(bool restart) +{ + int rc = 0, saved_page; + u16 data = 0; + + if (restart) + data = MII_88E1318S_PHY_LED_POL_LED2; + + saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE); + if (saved_page < 0) + goto err; + + /* Set the LED[2].0 Polarity bit to the required state */ + __phy_modify(phydev, MII_PHY_LED_POL_CTRL, + MII_88E1318S_PHY_LED_POL_LED2, data); + + if (!data) { + /* If WOL was enabled and a magic packet was received before powering + * off, we won't be able to wake up by sending another magic packet. + * Clear WOL status. + */ + __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE); + __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL, + MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); + } +err: + rc = phy_restore_page(phydev, saved_page, rc); + if (rc < 0) + dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc); +} + static const struct power_off_cfg linkstation_power_off_cfg = { .mdio_node_name = "mdio", .phy_set_reg = linkstation_mvphy_reg_intn, }; +static const struct power_off_cfg readynas_power_off_cfg = { + .mdio_node_name = "mdio-bus", + .phy_set_reg = readynas_mvphy_set_reg, +}; + static int linkstation_reboot_notifier(struct notifier_block *nb, unsigned long action, void *unused) { @@ -109,6 +148,9 @@ static const struct of_device_id ls_poweroff_of_match[] = { { .compatible = "buffalo,ls421de", .data = &linkstation_power_off_cfg, }, + { .compatible = "netgear,readynas-duo-v2", + .data = &readynas_power_off_cfg, + }, { }, }; From b171cb623ca253856b7bf7345e8761a7f24b54b9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 5 Aug 2021 10:58:27 +0200 Subject: [PATCH 136/177] dt-bindings: power: Extend battery bindings with chemistry This adds a battery-chemistry property and bindings for the different "technologies" that are used in Linux. More types can be added. This is needed to convert the custom ST-Ericsson AB8500 battery properties over to the generic battery bindings. Cc: devicetree@vger.kernel.org Signed-off-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/battery.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/battery.yaml b/Documentation/devicetree/bindings/power/supply/battery.yaml index c3b4b7543591..d56ac484fec5 100644 --- a/Documentation/devicetree/bindings/power/supply/battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/battery.yaml @@ -31,6 +31,20 @@ properties: compatible: const: simple-battery + device-chemistry: + description: This describes the chemical technology of the battery. + oneOf: + - const: nickel-cadmium + - const: nickel-metal-hydride + - const: lithium-ion + description: This is a blanket type for all lithium-ion batteries, + including those below. If possible, a precise compatible string + from below should be used, but sometimes it is unknown which specific + lithium ion battery is employed and this wide compatible can be used. + - const: lithium-ion-polymer + - const: lithium-ion-iron-phosphate + - const: lithium-ion-manganese-oxide + over-voltage-threshold-microvolt: description: battery over-voltage limit From 4eef766b7d4d88f0b984781bc1bcb574a6eafdc7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 5 Aug 2021 10:58:28 +0200 Subject: [PATCH 137/177] power: supply: core: Parse battery chemistry/technology This extends the struct power_supply_battery_info with a "technology" field makes the core DT parser optionally obtain this from the device tree. Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 20 ++++++++++++++++++++ include/linux/power_supply.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index d99e2f11c183..dd62c871b2b5 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -571,6 +571,7 @@ int power_supply_get_battery_info(struct power_supply *psy, int err, len, index; const __be32 *list; + info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; info->energy_full_design_uwh = -EINVAL; info->charge_full_design_uah = -EINVAL; info->voltage_min_design_uv = -EINVAL; @@ -618,6 +619,25 @@ int power_supply_get_battery_info(struct power_supply *psy, * Documentation/power/power_supply_class.rst. */ + err = of_property_read_string(battery_np, "device-chemistry", &value); + if (!err) { + if (!strcmp("nickel-cadmium", value)) + info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + else if (!strcmp("nickel-metal-hydride", value)) + info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + else if (!strcmp("lithium-ion", value)) + /* Imprecise lithium-ion type */ + info->technology = POWER_SUPPLY_TECHNOLOGY_LION; + else if (!strcmp("lithium-ion-polymer", value)) + info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + else if (!strcmp("lithium-ion-iron-phosphate", value)) + info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + else if (!strcmp("lithium-ion-manganese-oxide", value)) + info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + else + dev_warn(&psy->dev, "%s unknown battery type\n", value); + } + of_property_read_u32(battery_np, "energy-full-design-microwatt-hours", &info->energy_full_design_uwh); of_property_read_u32(battery_np, "charge-full-design-microamp-hours", diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index be203985ecdd..9ca1f120a211 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -352,6 +352,7 @@ struct power_supply_resistance_temp_table { */ struct power_supply_battery_info { + unsigned int technology; /* from the enum above */ int energy_full_design_uwh; /* microWatt-hours */ int charge_full_design_uah; /* microAmp-hours */ int voltage_min_design_uv; /* microVolts */ From 54784ffa5b267f57161eb8fbb811499f22a0a0bf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Aug 2021 10:27:14 +0200 Subject: [PATCH 138/177] power: supply: max17042: handle fails of reading status register Reading status register can fail in the interrupt handler. In such case, the regmap_read() will not store anything useful under passed 'val' variable and random stack value will be used to determine type of interrupt. Handle the regmap_read() failure to avoid handling interrupt type and triggering changed power supply event based on random stack value. Fixes: 39e7213edc4f ("max17042_battery: Support regmap to access device's registers") Cc: Signed-off-by: Krzysztof Kozlowski Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index f28c90ea41b4..62eb5ae36bb0 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -869,8 +869,12 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) { struct max17042_chip *chip = dev; u32 val; + int ret; + + ret = regmap_read(chip->regmap, MAX17042_STATUS, &val); + if (ret) + return IRQ_HANDLED; - regmap_read(chip->regmap, MAX17042_STATUS, &val); if ((val & STATUS_INTR_SOCMIN_BIT) || (val & STATUS_INTR_SOCMAX_BIT)) { dev_info(&chip->client->dev, "SOC threshold INTR\n"); From 22b6907caf1191f54dd0f4568414076ab479b7d3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Aug 2021 10:27:15 +0200 Subject: [PATCH 139/177] power: supply: max17042: remove duplicated STATUS bit defines All bits of STATUS register are already defined (see STATUS_SMN_BIT and further) so there is no need to define status SoC threshold min/max values one more time. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 62eb5ae36bb0..c6078f179fb3 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -36,8 +36,6 @@ /* Interrupt mask bits */ #define CONFIG_ALRT_BIT_ENBL (1 << 2) -#define STATUS_INTR_SOCMIN_BIT (1 << 10) -#define STATUS_INTR_SOCMAX_BIT (1 << 14) #define VFSOC0_LOCK 0x0000 #define VFSOC0_UNLOCK 0x0080 @@ -875,8 +873,7 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) if (ret) return IRQ_HANDLED; - if ((val & STATUS_INTR_SOCMIN_BIT) || - (val & STATUS_INTR_SOCMAX_BIT)) { + if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) { dev_info(&chip->client->dev, "SOC threshold INTR\n"); max17042_set_soc_threshold(chip, 1); } From 9c425fa3f273d63539533b14940d08582edd427c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Aug 2021 10:27:16 +0200 Subject: [PATCH 140/177] dt-bindings: power: supply: max17042: describe interrupt The Maxim 17042-family of fuel gauges are often embedded in other Maxim chips, e.g. in Maxim 77693 which is a companion power management IC. In such designs there might be actually two interrupts: - INTB signaling change from charger, flash or MUIC, - ALERT signaling change from fuel gauge. Describe the interrupt in bindings to make it clear it is about the fuel gauge ALERT interrupt, not the INT. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/maxim,max17042.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml index 42ebf87d300b..971b53c58cc6 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml @@ -26,6 +26,8 @@ properties: interrupts: maxItems: 1 + description: | + The ALRT pin, an open-drain interrupt. maxim,rsns-microohm: $ref: /schemas/types.yaml#/definitions/uint32 From ed14666c3f877c4c2a428a92bfeebfba3a4cfe2e Mon Sep 17 00:00:00 2001 From: Nathan Rossi Date: Mon, 16 Aug 2021 05:02:28 +0000 Subject: [PATCH 141/177] spi: orion: Prevent incorrect chip select behaviour When clearing the chip-select mask, the controller will switch to chip selecting the native CS0 line. Because the control register chip-select mask is not updated in a single write this will cause undesirable chip-selection of CS0 even when requesting to select other native chip-select lines. This is additionally problematic as the chip-select may still be asserted. With the ARMADA 38x SoC the controller will assert both the desired native chip-select and CS0. To avoid any undesirable behaviour with the chip-select lines, update the control register with a single write. This avoids selecting CS0 and causes the (de-)assert to apply at the same time. Signed-off-by: Nathan Rossi Link: https://lore.kernel.org/r/20210816050228.3223661-1-nathan@nathanrossi.com Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 34b31aba3981..e8de3cbbfb2a 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -328,8 +328,16 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) static void orion_spi_set_cs(struct spi_device *spi, bool enable) { struct orion_spi *orion_spi; + void __iomem *ctrl_reg; + u32 val; orion_spi = spi_master_get_devdata(spi->master); + ctrl_reg = spi_reg(orion_spi, ORION_SPI_IF_CTRL_REG); + + val = readl(ctrl_reg); + + /* Clear existing chip-select and assertion state */ + val &= ~(ORION_SPI_CS_MASK | 0x1); /* * If this line is using a GPIO to control chip select, this internal @@ -338,9 +346,7 @@ static void orion_spi_set_cs(struct spi_device *spi, bool enable) * as it is handled by a GPIO, but that doesn't matter. What we need * is to deassert the old chip select and assert some other chip select. */ - orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK); - orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, - ORION_SPI_CS(spi->chip_select)); + val |= ORION_SPI_CS(spi->chip_select); /* * Chip select logic is inverted from spi_set_cs(). For lines using a @@ -350,9 +356,13 @@ static void orion_spi_set_cs(struct spi_device *spi, bool enable) * doesn't matter. */ if (!enable) - orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); - else - orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); + val |= 0x1; + + /* + * To avoid toggling unwanted chip selects update the register + * with a single write. + */ + writel(val, ctrl_reg); } static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi) From 80698507e0b20817ab850538080b01c3e0a5314f Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 27 Jul 2021 11:25:53 +0200 Subject: [PATCH 142/177] power: reset: Add TPS65086 restart driver The only way to reset the BeagleV Starlight v0.9 board[1] properly is to tell the PMIC to reset itself which will then assert the external reset lines of the SoC, USB hub and ethernet phy. This adds a driver to register a reset handler to do just that. [1] https://github.com/beagleboard/beaglev-starlight Signed-off-by: Emil Renner Berthing Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 6 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/tps65086-restart.c | 98 ++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 drivers/power/reset/tps65086-restart.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 4d1192062508..4b563db3ab3e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -204,6 +204,12 @@ config POWER_RESET_ST help Reset support for STMicroelectronics boards. +config POWER_RESET_TPS65086 + bool "TPS65086 restart driver" + depends on MFD_TPS65086 + help + This driver adds support for resetting the TPS65086 PMIC on restart. + config POWER_RESET_VERSATILE bool "ARM Versatile family reboot driver" depends on ARM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index cf3f4d02d8a5..f606a2f60539 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o +obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o diff --git a/drivers/power/reset/tps65086-restart.c b/drivers/power/reset/tps65086-restart.c new file mode 100644 index 000000000000..78b89f745a3d --- /dev/null +++ b/drivers/power/reset/tps65086-restart.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include + +struct tps65086_restart { + struct notifier_block handler; + struct device *dev; +}; + +static int tps65086_restart_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct tps65086_restart *tps65086_restart = + container_of(this, struct tps65086_restart, handler); + struct tps65086 *tps65086 = dev_get_drvdata(tps65086_restart->dev->parent); + int ret; + + ret = regmap_write(tps65086->regmap, TPS65086_FORCESHUTDN, 1); + if (ret) { + dev_err(tps65086_restart->dev, "%s: error writing to tps65086 pmic: %d\n", + __func__, ret); + return NOTIFY_DONE; + } + + /* give it a little time */ + mdelay(200); + + WARN_ON(1); + + return NOTIFY_DONE; +} + +static int tps65086_restart_probe(struct platform_device *pdev) +{ + struct tps65086_restart *tps65086_restart; + int ret; + + tps65086_restart = devm_kzalloc(&pdev->dev, sizeof(*tps65086_restart), GFP_KERNEL); + if (!tps65086_restart) + return -ENOMEM; + + platform_set_drvdata(pdev, tps65086_restart); + + tps65086_restart->handler.notifier_call = tps65086_restart_notify; + tps65086_restart->handler.priority = 192; + tps65086_restart->dev = &pdev->dev; + + ret = register_restart_handler(&tps65086_restart->handler); + if (ret) { + dev_err(&pdev->dev, "%s: cannot register restart handler: %d\n", + __func__, ret); + return -ENODEV; + } + + return 0; +} + +static int tps65086_restart_remove(struct platform_device *pdev) +{ + struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev); + int ret; + + ret = unregister_restart_handler(&tps65086_restart->handler); + if (ret) { + dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n", + __func__, ret); + return -ENODEV; + } + + return 0; +} + +static const struct platform_device_id tps65086_restart_id_table[] = { + { "tps65086-reset", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table); + +static struct platform_driver tps65086_restart_driver = { + .driver = { + .name = "tps65086-restart", + }, + .probe = tps65086_restart_probe, + .remove = tps65086_restart_remove, + .id_table = tps65086_restart_id_table, +}; +module_platform_driver(tps65086_restart_driver); + +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_DESCRIPTION("TPS65086 restart driver"); +MODULE_LICENSE("GPL v2"); From 3e81bd7dfb9c72679666d1a54c9dd94b711f0fbc Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 20:38:35 +0300 Subject: [PATCH 143/177] dt-bindings: power: supply: smb347-charger: Document USB VBUS regulator SMB347 can supply power to USB VBUS, which is required by OTG-cable devices that want to switch USB port into the host mode. Add USB VBUS regulator properties. Reviewed-by: Rob Herring Signed-off-by: Dmitry Osipenko Signed-off-by: Sebastian Reichel --- .../power/supply/summit,smb347-charger.yaml | 30 +++++++++++++++++++ .../dt-bindings/power/summit,smb347-charger.h | 4 +++ 2 files changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml index 983fc215c1e5..20862cdfc116 100644 --- a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml @@ -73,6 +73,26 @@ properties: - 1 # SMB3XX_SOFT_TEMP_COMPENSATE_CURRENT Current compensation - 2 # SMB3XX_SOFT_TEMP_COMPENSATE_VOLTAGE Voltage compensation + summit,inok-polarity: + description: | + Polarity of INOK signal indicating presence of external power supply. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # SMB3XX_SYSOK_INOK_ACTIVE_LOW + - 1 # SMB3XX_SYSOK_INOK_ACTIVE_HIGH + + usb-vbus: + $ref: "../../regulator/regulator.yaml#" + type: object + + properties: + summit,needs-inok-toggle: + type: boolean + description: INOK signal is fixed and polarity needs to be toggled + in order to enable/disable output mode. + + unevaluatedProperties: false + allOf: - if: properties: @@ -134,6 +154,7 @@ examples: reg = <0x7f>; summit,enable-charge-control = ; + summit,inok-polarity = ; summit,chip-temperature-threshold-celsius = <110>; summit,mains-current-limit-microamp = <2000000>; summit,usb-current-limit-microamp = <500000>; @@ -141,6 +162,15 @@ examples: summit,enable-mains-charging; monitored-battery = <&battery>; + + usb-vbus { + regulator-name = "usb_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-min-microamp = <750000>; + regulator-max-microamp = <750000>; + summit,needs-inok-toggle; + }; }; }; diff --git a/include/dt-bindings/power/summit,smb347-charger.h b/include/dt-bindings/power/summit,smb347-charger.h index d918bf321a71..3205699b5e41 100644 --- a/include/dt-bindings/power/summit,smb347-charger.h +++ b/include/dt-bindings/power/summit,smb347-charger.h @@ -16,4 +16,8 @@ #define SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW 1 #define SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH 2 +/* Polarity of INOK signal */ +#define SMB3XX_SYSOK_INOK_ACTIVE_LOW 0 +#define SMB3XX_SYSOK_INOK_ACTIVE_HIGH 1 + #endif From 4ac59d85a2369eac88f6042e3b9b8a4789525909 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 20:38:36 +0300 Subject: [PATCH 144/177] power: supply: smb347-charger: Make smb347_set_writable() IRQ-safe The smb347_set_writable() is used by interrupt handler and outside of it. The interrupt should be disabled when the function is used outside of interrupt handler in order to prevent racing with the interrupt context. Add new parameter to smb347_set_writable() that allows to disable IRQ. Signed-off-by: Dmitry Osipenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/smb347-charger.c | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index df240420f2de..db1378b41f80 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -671,10 +671,22 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) * * Returns %0 on success and negative errno in case of failure. */ -static int smb347_set_writable(struct smb347_charger *smb, bool writable) +static int smb347_set_writable(struct smb347_charger *smb, bool writable, + bool irq_toggle) { - return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, - writable ? CMD_A_ALLOW_WRITE : 0); + struct i2c_client *client = to_i2c_client(smb->dev); + int ret; + + if (writable && irq_toggle && !smb->irq_unsupported) + disable_irq(client->irq); + + ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, + writable ? CMD_A_ALLOW_WRITE : 0); + + if ((!writable || ret) && irq_toggle && !smb->irq_unsupported) + enable_irq(client->irq); + + return ret; } static int smb347_hw_init(struct smb347_charger *smb) @@ -682,7 +694,7 @@ static int smb347_hw_init(struct smb347_charger *smb) unsigned int val; int ret; - ret = smb347_set_writable(smb, true); + ret = smb347_set_writable(smb, true, false); if (ret < 0) return ret; @@ -758,7 +770,7 @@ static int smb347_hw_init(struct smb347_charger *smb) ret = smb347_start_stop_charging(smb); fail: - smb347_set_writable(smb, false); + smb347_set_writable(smb, false, false); return ret; } @@ -866,7 +878,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) if (smb->irq_unsupported) return 0; - ret = smb347_set_writable(smb, true); + ret = smb347_set_writable(smb, true, true); if (ret < 0) return ret; @@ -891,7 +903,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, enable ? CFG_PIN_EN_CHARGER_ERROR : 0); fail: - smb347_set_writable(smb, false); + smb347_set_writable(smb, false, true); return ret; } @@ -919,7 +931,7 @@ static int smb347_irq_init(struct smb347_charger *smb, if (!client->irq) return 0; - ret = smb347_set_writable(smb, true); + ret = smb347_set_writable(smb, true, false); if (ret < 0) return ret; @@ -931,7 +943,7 @@ static int smb347_irq_init(struct smb347_charger *smb, CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, CFG_STAT_DISABLED); - smb347_set_writable(smb, false); + smb347_set_writable(smb, false, false); if (ret < 0) { dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret); From 17e7bc532cd540d0e6e2fa1af19faf74ba252051 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 20:38:37 +0300 Subject: [PATCH 145/177] power: supply: smb347-charger: Utilize generic regmap caching Utilize generic regmap caching in order to avoid unnecessary slow I2C accesses to all constant registers each time the supply status updated and remove local caching of charger state to make code cleaner. Signed-off-by: Dmitry Osipenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/smb347-charger.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index db1378b41f80..27254e6efdde 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -135,7 +135,6 @@ * @id: SMB charger ID * @mains_online: is AC/DC input connected * @usb_online: is USB input connected - * @charging_enabled: is charging enabled * @irq_unsupported: is interrupt unsupported by SMB hardware * @max_charge_current: maximum current (in uA) the battery can be charged * @max_charge_voltage: maximum voltage (in uV) the battery can be charged @@ -192,7 +191,6 @@ struct smb347_charger { unsigned int id; bool mains_online; bool usb_online; - bool charging_enabled; bool irq_unsupported; unsigned int max_charge_current; @@ -358,21 +356,13 @@ static int smb347_charging_status(struct smb347_charger *smb) static int smb347_charging_set(struct smb347_charger *smb, bool enable) { - int ret = 0; - if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) { dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); return 0; } - if (smb->charging_enabled != enable) { - ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, - enable ? CMD_A_CHG_ENABLED : 0); - if (!ret) - smb->charging_enabled = enable; - } - - return ret; + return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, + enable ? CMD_A_CHG_ENABLED : 0); } static inline int smb347_charging_enable(struct smb347_charger *smb) @@ -1310,6 +1300,8 @@ static const struct regmap_config smb347_regmap = { .max_register = SMB347_MAX_REGISTER, .volatile_reg = smb347_volatile_reg, .readable_reg = smb347_readable_reg, + .cache_type = REGCACHE_FLAT, + .num_reg_defaults_raw = SMB347_MAX_REGISTER, }; static const struct power_supply_desc smb347_mains_desc = { From efe2175478d5237949e33c84d9a722fc084b218c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 20:38:38 +0300 Subject: [PATCH 146/177] power: supply: smb347-charger: Add missing pin control activation Pin control needs to be activated by setting the enable bit, otherwise hardware rejects all pin changes. Previously this stayed unnoticed on Nexus 7 because pin control was enabled by default after rebooting from downstream kernel, which uses driver that enables the bit and charger registers are non-volatile until power supply (battery) is disconnected. Configure the pin control enable bit. This fixes the potentially never-enabled charging on devices that use pin control. Signed-off-by: Dmitry Osipenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/smb347-charger.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 27254e6efdde..1c9205ca0993 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -55,6 +55,7 @@ #define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 #define CFG_PIN_EN_APSD_IRQ BIT(1) #define CFG_PIN_EN_CHARGER_ERROR BIT(2) +#define CFG_PIN_EN_CTRL BIT(4) #define CFG_THERM 0x07 #define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03 #define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0 @@ -726,6 +727,15 @@ static int smb347_hw_init(struct smb347_charger *smb) if (ret < 0) goto fail; + /* Activate pin control, making it writable. */ + switch (smb->enable_control) { + case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: + case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: + ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL); + if (ret < 0) + goto fail; + } + /* * Make the charging functionality controllable by a write to the * command register unless pin control is specified in the platform From 565efae96ca1bd5405dac88d2284cd2167e5d5f0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 31 Jul 2021 20:38:39 +0300 Subject: [PATCH 147/177] power: supply: smb347-charger: Implement USB VBUS regulator SMB347 can supply power to USB VBUS, implement the USB VBUS regulator. USB VBUS needs to be powered for switching OTG-cable USB port into host mode. Signed-off-by: Dmitry Osipenko Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 1 + drivers/power/supply/smb347-charger.c | 219 ++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 793a6f8ca080..fcc7534edcb2 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -680,6 +680,7 @@ config CHARGER_BQ256XX config CHARGER_SMB347 tristate "Summit Microelectronics SMB3XX Battery Charger" depends on I2C + depends on REGULATOR select REGMAP_I2C help Say Y to include support for Summit Microelectronics SMB345, diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 1c9205ca0993..753944e774c4 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -63,12 +64,15 @@ #define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2 #define CFG_THERM_MONITOR_DISABLED BIT(4) #define CFG_SYSOK 0x08 +#define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0) #define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2) #define CFG_OTHER 0x09 #define CFG_OTHER_RID_MASK 0xc0 #define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0 #define CFG_OTG 0x0a #define CFG_OTG_TEMP_THRESHOLD_MASK 0x30 +#define CFG_OTG_CURRENT_LIMIT_250mA BIT(2) +#define CFG_OTG_CURRENT_LIMIT_750mA BIT(3) #define CFG_OTG_TEMP_THRESHOLD_SHIFT 4 #define CFG_OTG_CC_COMPENSATION_MASK 0xc0 #define CFG_OTG_CC_COMPENSATION_SHIFT 6 @@ -92,6 +96,7 @@ #define CMD_A 0x30 #define CMD_A_CHG_ENABLED BIT(1) #define CMD_A_SUSPEND_ENABLED BIT(2) +#define CMD_A_OTG_ENABLED BIT(4) #define CMD_A_ALLOW_WRITE BIT(7) #define CMD_B 0x31 #define CMD_C 0x33 @@ -133,10 +138,12 @@ * @regmap: pointer to driver regmap * @mains: power_supply instance for AC/DC power * @usb: power_supply instance for USB power + * @usb_rdev: USB VBUS regulator device * @id: SMB charger ID * @mains_online: is AC/DC input connected * @usb_online: is USB input connected * @irq_unsupported: is interrupt unsupported by SMB hardware + * @usb_vbus_enabled: is USB VBUS powered by SMB charger * @max_charge_current: maximum current (in uA) the battery can be charged * @max_charge_voltage: maximum voltage (in uV) the battery can be charged * @pre_charge_current: current (in uA) to use in pre-charging phase @@ -167,6 +174,8 @@ * @use_usb_otg: USB OTG output can be used (not implemented yet) * @enable_control: how charging enable/disable is controlled * (driver/pin controls) + * @inok_polarity: polarity of INOK signal which denotes presence of external + * power supply * * @use_main, @use_usb, and @use_usb_otg are means to enable/disable * hardware support for these. This is useful when we want to have for @@ -189,10 +198,12 @@ struct smb347_charger { struct regmap *regmap; struct power_supply *mains; struct power_supply *usb; + struct regulator_dev *usb_rdev; unsigned int id; bool mains_online; bool usb_online; bool irq_unsupported; + bool usb_vbus_enabled; unsigned int max_charge_current; unsigned int max_charge_voltage; @@ -213,6 +224,7 @@ struct smb347_charger { bool use_usb; bool use_usb_otg; unsigned int enable_control; + unsigned int inok_polarity; }; enum smb_charger_chipid { @@ -362,6 +374,11 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) return 0; } + if (enable && smb->usb_vbus_enabled) { + dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n"); + return 0; + } + return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, enable ? CMD_A_CHG_ENABLED : 0); } @@ -1253,6 +1270,13 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb) /* Select charging control */ device_property_read_u32(dev, "summit,enable-charge-control", &smb->enable_control); + + /* + * Polarity of INOK signal indicating presence of external power + * supply connected to the charger. + */ + device_property_read_u32(dev, "summit,inok-polarity", + &smb->inok_polarity); } static int smb347_get_battery_info(struct smb347_charger *smb) @@ -1304,6 +1328,160 @@ static int smb347_get_battery_info(struct smb347_charger *smb) return 0; } +static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev) +{ + struct smb347_charger *smb = rdev_get_drvdata(rdev); + unsigned int val; + int ret; + + ret = regmap_read(smb->regmap, CFG_OTG, &val); + if (ret < 0) + return ret; + + /* + * It's unknown what happens if this bit is unset due to lack of + * access to the datasheet, assume it's limit-enable. + */ + if (!(val & CFG_OTG_CURRENT_LIMIT_250mA)) + return 0; + + return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000; +} + +static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb, + int max_uA) +{ + const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA | + CFG_OTG_CURRENT_LIMIT_250mA; + unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA; + int ret; + + if (max_uA >= 750000) + val |= CFG_OTG_CURRENT_LIMIT_750mA; + + ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val); + if (ret < 0) + dev_err(smb->dev, "failed to change USB current limit\n"); + + return ret; +} + +static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct smb347_charger *smb = rdev_get_drvdata(rdev); + int ret; + + ret = smb347_set_writable(smb, true, true); + if (ret < 0) + return ret; + + ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA); + smb347_set_writable(smb, false, true); + + return ret; +} + +static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev) +{ + struct smb347_charger *smb = rdev_get_drvdata(rdev); + int ret, max_uA; + + ret = smb347_set_writable(smb, true, true); + if (ret < 0) + return ret; + + smb347_charging_disable(smb); + + if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) { + unsigned int sysok = 0; + + if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW) + sysok = CFG_SYSOK_INOK_ACTIVE_HIGH; + + /* + * VBUS won't be powered if INOK is active, so we need to + * manually disable INOK on some platforms. + */ + ret = regmap_update_bits(smb->regmap, CFG_SYSOK, + CFG_SYSOK_INOK_ACTIVE_HIGH, sysok); + if (ret < 0) { + dev_err(smb->dev, "failed to disable INOK\n"); + goto done; + } + } + + ret = smb347_usb_vbus_get_current_limit(rdev); + if (ret < 0) { + dev_err(smb->dev, "failed to get USB VBUS current limit\n"); + goto done; + } + + max_uA = ret; + + ret = smb347_usb_vbus_set_new_current_limit(smb, 250000); + if (ret < 0) { + dev_err(smb->dev, "failed to preset USB VBUS current limit\n"); + goto done; + } + + ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED); + if (ret < 0) { + dev_err(smb->dev, "failed to enable USB VBUS\n"); + goto done; + } + + smb->usb_vbus_enabled = true; + + ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA); + if (ret < 0) { + dev_err(smb->dev, "failed to restore USB VBUS current limit\n"); + goto done; + } +done: + smb347_set_writable(smb, false, true); + + return ret; +} + +static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev) +{ + struct smb347_charger *smb = rdev_get_drvdata(rdev); + int ret; + + ret = smb347_set_writable(smb, true, true); + if (ret < 0) + return ret; + + ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED); + if (ret < 0) { + dev_err(smb->dev, "failed to disable USB VBUS\n"); + goto done; + } + + smb->usb_vbus_enabled = false; + + if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) { + unsigned int sysok = 0; + + if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH) + sysok = CFG_SYSOK_INOK_ACTIVE_HIGH; + + ret = regmap_update_bits(smb->regmap, CFG_SYSOK, + CFG_SYSOK_INOK_ACTIVE_HIGH, sysok); + if (ret < 0) { + dev_err(smb->dev, "failed to enable INOK\n"); + goto done; + } + } + + smb347_start_stop_charging(smb); +done: + smb347_set_writable(smb, false, true); + + return ret; +} + static const struct regmap_config smb347_regmap = { .reg_bits = 8, .val_bits = 8, @@ -1314,6 +1492,14 @@ static const struct regmap_config smb347_regmap = { .num_reg_defaults_raw = SMB347_MAX_REGISTER, }; +static const struct regulator_ops smb347_usb_vbus_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = smb347_usb_vbus_regulator_enable, + .disable = smb347_usb_vbus_regulator_disable, + .get_current_limit = smb347_usb_vbus_get_current_limit, + .set_current_limit = smb347_usb_vbus_set_current_limit, +}; + static const struct power_supply_desc smb347_mains_desc = { .name = "smb347-mains", .type = POWER_SUPPLY_TYPE_MAINS, @@ -1330,10 +1516,24 @@ static const struct power_supply_desc smb347_usb_desc = { .num_properties = ARRAY_SIZE(smb347_properties), }; +static const struct regulator_desc smb347_usb_vbus_regulator_desc = { + .name = "smb347-usb-vbus", + .of_match = of_match_ptr("usb-vbus"), + .ops = &smb347_usb_vbus_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = CMD_A, + .enable_mask = CMD_A_OTG_ENABLED, + .enable_val = CMD_A_OTG_ENABLED, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + static int smb347_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct power_supply_config mains_usb_cfg = {}; + struct regulator_config usb_rdev_cfg = {}; struct device *dev = &client->dev; struct smb347_charger *smb; int ret; @@ -1381,6 +1581,18 @@ static int smb347_probe(struct i2c_client *client, if (ret) return ret; + usb_rdev_cfg.dev = dev; + usb_rdev_cfg.driver_data = smb; + usb_rdev_cfg.regmap = smb->regmap; + + smb->usb_rdev = devm_regulator_register(dev, + &smb347_usb_vbus_regulator_desc, + &usb_rdev_cfg); + if (IS_ERR(smb->usb_rdev)) { + smb347_irq_disable(smb); + return PTR_ERR(smb->usb_rdev); + } + return 0; } @@ -1388,11 +1600,17 @@ static int smb347_remove(struct i2c_client *client) { struct smb347_charger *smb = i2c_get_clientdata(client); + smb347_usb_vbus_regulator_disable(smb->usb_rdev); smb347_irq_disable(smb); return 0; } +static void smb347_shutdown(struct i2c_client *client) +{ + smb347_remove(client); +} + static const struct i2c_device_id smb347_id[] = { { "smb345", SMB345 }, { "smb347", SMB347 }, @@ -1416,6 +1634,7 @@ static struct i2c_driver smb347_driver = { }, .probe = smb347_probe, .remove = smb347_remove, + .shutdown = smb347_shutdown, .id_table = smb347_id, }; module_i2c_driver(smb347_driver); From 538d7c2ed73098850fe80be14eed2739d37e419b Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Thu, 12 Aug 2021 21:45:41 +0800 Subject: [PATCH 148/177] spi: rockchip-sfc: Bindings for Rockchip serial flash controller Add bindings for the Rockchip serial flash controller. New device specific parameter of rockchip,sfc-no-dma included in documentation. Signed-off-by: Chris Morgan Signed-off-by: Jon Lin Tested-by: Peter Geis Link: https://lore.kernel.org/r/20210812134546.31340-2-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/rockchip-sfc.yaml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/rockchip-sfc.yaml diff --git a/Documentation/devicetree/bindings/spi/rockchip-sfc.yaml b/Documentation/devicetree/bindings/spi/rockchip-sfc.yaml new file mode 100644 index 000000000000..339fb39529f3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/rockchip-sfc.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/rockchip-sfc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip Serial Flash Controller (SFC) + +maintainers: + - Heiko Stuebner + - Chris Morgan + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: rockchip,sfc + description: + The rockchip sfc controller is a standalone IP with version register, + and the driver can handle all the feature difference inside the IP + depending on the version register. + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Bus Clock + - description: Module Clock + + clock-names: + items: + - const: clk_sfc + - const: hclk_sfc + + power-domains: + maxItems: 1 + + rockchip,sfc-no-dma: + description: Disable DMA and utilize FIFO mode only + type: boolean + +patternProperties: + "^flash@[0-3]$": + type: object + properties: + reg: + minimum: 0 + maximum: 3 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + sfc: spi@ff3a0000 { + compatible = "rockchip,sfc"; + reg = <0xff3a0000 0x4000>; + interrupts = ; + clocks = <&cru SCLK_SFC>, <&cru HCLK_SFC>; + clock-names = "clk_sfc", "hclk_sfc"; + pinctrl-0 = <&sfc_clk &sfc_cs &sfc_bus2>; + pinctrl-names = "default"; + power-domains = <&power PX30_PD_MMC_NAND>; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <108000000>; + spi-rx-bus-width = <2>; + spi-tx-bus-width = <2>; + }; + }; + +... From 0b89fc0a367edab09065af722894d186bd0ccb0d Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Thu, 12 Aug 2021 21:45:42 +0800 Subject: [PATCH 149/177] spi: rockchip-sfc: add rockchip serial flash controller Add the rockchip serial flash controller (SFC) driver. Signed-off-by: Chris Morgan Signed-off-by: Jon Lin Tested-by: Peter Geis Tested-by: Chris Morgan Link: https://lore.kernel.org/r/20210812134546.31340-3-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 12 + drivers/spi/Makefile | 1 + drivers/spi/spi-rockchip-sfc.c | 694 +++++++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+) create mode 100644 drivers/spi/spi-rockchip-sfc.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e71a4c514f7b..83e352b0c8f9 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -658,6 +658,18 @@ config SPI_ROCKCHIP The main usecase of this controller is to use spi flash as boot device. +config SPI_ROCKCHIP_SFC + tristate "Rockchip Serial Flash Controller (SFC)" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on HAS_IOMEM && HAS_DMA + help + This enables support for Rockchip serial flash controller. This + is a specialized controller used to access SPI flash on some + Rockchip SOCs. + + ROCKCHIP SFC supports DMA and PIO modes. When DMA is not available, + the driver automatically falls back to PIO mode. + config SPI_RB4XX tristate "Mikrotik RB4XX SPI master" depends on SPI_MASTER && ATH79 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 13e54c45e9df..699db95c8441 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o +obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c new file mode 100644 index 000000000000..7c4d47fe80c2 --- /dev/null +++ b/drivers/spi/spi-rockchip-sfc.c @@ -0,0 +1,694 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Rockchip Serial Flash Controller Driver + * + * Copyright (c) 2017-2021, Rockchip Inc. + * Author: Shawn Lin + * Chris Morgan + * Jon Lin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* System control */ +#define SFC_CTRL 0x0 +#define SFC_CTRL_PHASE_SEL_NEGETIVE BIT(1) +#define SFC_CTRL_CMD_BITS_SHIFT 8 +#define SFC_CTRL_ADDR_BITS_SHIFT 10 +#define SFC_CTRL_DATA_BITS_SHIFT 12 + +/* Interrupt mask */ +#define SFC_IMR 0x4 +#define SFC_IMR_RX_FULL BIT(0) +#define SFC_IMR_RX_UFLOW BIT(1) +#define SFC_IMR_TX_OFLOW BIT(2) +#define SFC_IMR_TX_EMPTY BIT(3) +#define SFC_IMR_TRAN_FINISH BIT(4) +#define SFC_IMR_BUS_ERR BIT(5) +#define SFC_IMR_NSPI_ERR BIT(6) +#define SFC_IMR_DMA BIT(7) + +/* Interrupt clear */ +#define SFC_ICLR 0x8 +#define SFC_ICLR_RX_FULL BIT(0) +#define SFC_ICLR_RX_UFLOW BIT(1) +#define SFC_ICLR_TX_OFLOW BIT(2) +#define SFC_ICLR_TX_EMPTY BIT(3) +#define SFC_ICLR_TRAN_FINISH BIT(4) +#define SFC_ICLR_BUS_ERR BIT(5) +#define SFC_ICLR_NSPI_ERR BIT(6) +#define SFC_ICLR_DMA BIT(7) + +/* FIFO threshold level */ +#define SFC_FTLR 0xc +#define SFC_FTLR_TX_SHIFT 0 +#define SFC_FTLR_TX_MASK 0x1f +#define SFC_FTLR_RX_SHIFT 8 +#define SFC_FTLR_RX_MASK 0x1f + +/* Reset FSM and FIFO */ +#define SFC_RCVR 0x10 +#define SFC_RCVR_RESET BIT(0) + +/* Enhanced mode */ +#define SFC_AX 0x14 + +/* Address Bit number */ +#define SFC_ABIT 0x18 + +/* Interrupt status */ +#define SFC_ISR 0x1c +#define SFC_ISR_RX_FULL_SHIFT BIT(0) +#define SFC_ISR_RX_UFLOW_SHIFT BIT(1) +#define SFC_ISR_TX_OFLOW_SHIFT BIT(2) +#define SFC_ISR_TX_EMPTY_SHIFT BIT(3) +#define SFC_ISR_TX_FINISH_SHIFT BIT(4) +#define SFC_ISR_BUS_ERR_SHIFT BIT(5) +#define SFC_ISR_NSPI_ERR_SHIFT BIT(6) +#define SFC_ISR_DMA_SHIFT BIT(7) + +/* FIFO status */ +#define SFC_FSR 0x20 +#define SFC_FSR_TX_IS_FULL BIT(0) +#define SFC_FSR_TX_IS_EMPTY BIT(1) +#define SFC_FSR_RX_IS_EMPTY BIT(2) +#define SFC_FSR_RX_IS_FULL BIT(3) +#define SFC_FSR_TXLV_MASK GENMASK(12, 8) +#define SFC_FSR_TXLV_SHIFT 8 +#define SFC_FSR_RXLV_MASK GENMASK(20, 16) +#define SFC_FSR_RXLV_SHIFT 16 + +/* FSM status */ +#define SFC_SR 0x24 +#define SFC_SR_IS_IDLE 0x0 +#define SFC_SR_IS_BUSY 0x1 + +/* Raw interrupt status */ +#define SFC_RISR 0x28 +#define SFC_RISR_RX_FULL BIT(0) +#define SFC_RISR_RX_UNDERFLOW BIT(1) +#define SFC_RISR_TX_OVERFLOW BIT(2) +#define SFC_RISR_TX_EMPTY BIT(3) +#define SFC_RISR_TRAN_FINISH BIT(4) +#define SFC_RISR_BUS_ERR BIT(5) +#define SFC_RISR_NSPI_ERR BIT(6) +#define SFC_RISR_DMA BIT(7) + +/* Version */ +#define SFC_VER 0x2C +#define SFC_VER_3 0x3 +#define SFC_VER_4 0x4 +#define SFC_VER_5 0x5 + +/* Delay line controller resiter */ +#define SFC_DLL_CTRL0 0x3C +#define SFC_DLL_CTRL0_SCLK_SMP_DLL BIT(15) +#define SFC_DLL_CTRL0_DLL_MAX_VER4 0xFFU +#define SFC_DLL_CTRL0_DLL_MAX_VER5 0x1FFU + +/* Master trigger */ +#define SFC_DMA_TRIGGER 0x80 +#define SFC_DMA_TRIGGER_START 1 + +/* Src or Dst addr for master */ +#define SFC_DMA_ADDR 0x84 + +/* Length control register extension 32GB */ +#define SFC_LEN_CTRL 0x88 +#define SFC_LEN_CTRL_TRB_SEL 1 +#define SFC_LEN_EXT 0x8C + +/* Command */ +#define SFC_CMD 0x100 +#define SFC_CMD_IDX_SHIFT 0 +#define SFC_CMD_DUMMY_SHIFT 8 +#define SFC_CMD_DIR_SHIFT 12 +#define SFC_CMD_DIR_RD 0 +#define SFC_CMD_DIR_WR 1 +#define SFC_CMD_ADDR_SHIFT 14 +#define SFC_CMD_ADDR_0BITS 0 +#define SFC_CMD_ADDR_24BITS 1 +#define SFC_CMD_ADDR_32BITS 2 +#define SFC_CMD_ADDR_XBITS 3 +#define SFC_CMD_TRAN_BYTES_SHIFT 16 +#define SFC_CMD_CS_SHIFT 30 + +/* Address */ +#define SFC_ADDR 0x104 + +/* Data */ +#define SFC_DATA 0x108 + +/* The controller and documentation reports that it supports up to 4 CS + * devices (0-3), however I have only been able to test a single CS (CS 0) + * due to the configuration of my device. + */ +#define SFC_MAX_CHIPSELECT_NUM 4 + +/* The SFC can transfer max 16KB - 1 at one time + * we set it to 15.5KB here for alignment. + */ +#define SFC_MAX_IOSIZE_VER3 (512 * 31) + +/* DMA is only enabled for large data transmission */ +#define SFC_DMA_TRANS_THRETHOLD (0x40) + +/* Maximum clock values from datasheet suggest keeping clock value under + * 150MHz. No minimum or average value is suggested. + */ +#define SFC_MAX_SPEED (150 * 1000 * 1000) + +struct rockchip_sfc { + struct device *dev; + void __iomem *regbase; + struct clk *hclk; + struct clk *clk; + u32 frequency; + /* virtual mapped addr for dma_buffer */ + void *buffer; + dma_addr_t dma_buffer; + struct completion cp; + bool use_dma; + u32 max_iosize; + u16 version; +}; + +static int rockchip_sfc_reset(struct rockchip_sfc *sfc) +{ + int err; + u32 status; + + writel_relaxed(SFC_RCVR_RESET, sfc->regbase + SFC_RCVR); + + err = readl_poll_timeout(sfc->regbase + SFC_RCVR, status, + !(status & SFC_RCVR_RESET), 20, + jiffies_to_usecs(HZ)); + if (err) + dev_err(sfc->dev, "SFC reset never finished\n"); + + /* Still need to clear the masked interrupt from RISR */ + writel_relaxed(0xFFFFFFFF, sfc->regbase + SFC_ICLR); + + dev_dbg(sfc->dev, "reset\n"); + + return err; +} + +static u16 rockchip_sfc_get_version(struct rockchip_sfc *sfc) +{ + return (u16)(readl(sfc->regbase + SFC_VER) & 0xffff); +} + +static u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc) +{ + return SFC_MAX_IOSIZE_VER3; +} + +static void rockchip_sfc_irq_unmask(struct rockchip_sfc *sfc, u32 mask) +{ + u32 reg; + + /* Enable transfer complete interrupt */ + reg = readl(sfc->regbase + SFC_IMR); + reg &= ~mask; + writel(reg, sfc->regbase + SFC_IMR); +} + +static void rockchip_sfc_irq_mask(struct rockchip_sfc *sfc, u32 mask) +{ + u32 reg; + + /* Disable transfer finish interrupt */ + reg = readl(sfc->regbase + SFC_IMR); + reg |= mask; + writel(reg, sfc->regbase + SFC_IMR); +} + +static int rockchip_sfc_init(struct rockchip_sfc *sfc) +{ + writel(0, sfc->regbase + SFC_CTRL); + writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR); + rockchip_sfc_irq_mask(sfc, 0xFFFFFFFF); + if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) + writel(SFC_LEN_CTRL_TRB_SEL, sfc->regbase + SFC_LEN_CTRL); + + return 0; +} + +static int rockchip_sfc_wait_txfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us) +{ + int ret = 0; + u32 status; + + ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status, + status & SFC_FSR_TXLV_MASK, 0, + timeout_us); + if (ret) { + dev_dbg(sfc->dev, "sfc wait tx fifo timeout\n"); + + ret = -ETIMEDOUT; + } + + return (status & SFC_FSR_TXLV_MASK) >> SFC_FSR_TXLV_SHIFT; +} + +static int rockchip_sfc_wait_rxfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us) +{ + int ret = 0; + u32 status; + + ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status, + status & SFC_FSR_RXLV_MASK, 0, + timeout_us); + if (ret) { + dev_dbg(sfc->dev, "sfc wait rx fifo timeout\n"); + + ret = -ETIMEDOUT; + } + + return (status & SFC_FSR_RXLV_MASK) >> SFC_FSR_RXLV_SHIFT; +} + +static void rockchip_sfc_adjust_op_work(struct spi_mem_op *op) +{ + if (unlikely(op->dummy.nbytes && !op->addr.nbytes)) { + /* + * SFC not support output DUMMY cycles right after CMD cycles, so + * treat it as ADDR cycles. + */ + op->addr.nbytes = op->dummy.nbytes; + op->addr.buswidth = op->dummy.buswidth; + op->addr.val = 0xFFFFFFFFF; + + op->dummy.nbytes = 0; + } +} + +static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc, + struct spi_mem *mem, + const struct spi_mem_op *op, + u32 len) +{ + u32 ctrl = 0, cmd = 0; + + /* set CMD */ + cmd = op->cmd.opcode; + ctrl |= ((op->cmd.buswidth >> 1) << SFC_CTRL_CMD_BITS_SHIFT); + + /* set ADDR */ + if (op->addr.nbytes) { + if (op->addr.nbytes == 4) { + cmd |= SFC_CMD_ADDR_32BITS << SFC_CMD_ADDR_SHIFT; + } else if (op->addr.nbytes == 3) { + cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT; + } else { + cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT; + writel(op->addr.nbytes * 8 - 1, sfc->regbase + SFC_ABIT); + } + + ctrl |= ((op->addr.buswidth >> 1) << SFC_CTRL_ADDR_BITS_SHIFT); + } + + /* set DUMMY */ + if (op->dummy.nbytes) { + if (op->dummy.buswidth == 4) + cmd |= op->dummy.nbytes * 2 << SFC_CMD_DUMMY_SHIFT; + else if (op->dummy.buswidth == 2) + cmd |= op->dummy.nbytes * 4 << SFC_CMD_DUMMY_SHIFT; + else + cmd |= op->dummy.nbytes * 8 << SFC_CMD_DUMMY_SHIFT; + } + + /* set DATA */ + if (sfc->version >= SFC_VER_4) /* Clear it if no data to transfer */ + writel(len, sfc->regbase + SFC_LEN_EXT); + else + cmd |= len << SFC_CMD_TRAN_BYTES_SHIFT; + if (len) { + if (op->data.dir == SPI_MEM_DATA_OUT) + cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT; + + ctrl |= ((op->data.buswidth >> 1) << SFC_CTRL_DATA_BITS_SHIFT); + } + if (!len && op->addr.nbytes) + cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT; + + /* set the Controller */ + ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE; + cmd |= mem->spi->chip_select << SFC_CMD_CS_SHIFT; + + dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n", + op->addr.nbytes, op->addr.buswidth, + op->dummy.nbytes, op->dummy.buswidth); + dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n", + ctrl, cmd, op->addr.val, len); + + writel(ctrl, sfc->regbase + SFC_CTRL); + writel(cmd, sfc->regbase + SFC_CMD); + if (op->addr.nbytes) + writel(op->addr.val, sfc->regbase + SFC_ADDR); + + return 0; +} + +static int rockchip_sfc_write_fifo(struct rockchip_sfc *sfc, const u8 *buf, int len) +{ + u8 bytes = len & 0x3; + u32 dwords; + int tx_level; + u32 write_words; + u32 tmp = 0; + + dwords = len >> 2; + while (dwords) { + tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000); + if (tx_level < 0) + return tx_level; + write_words = min_t(u32, tx_level, dwords); + iowrite32_rep(sfc->regbase + SFC_DATA, buf, write_words); + buf += write_words << 2; + dwords -= write_words; + } + + /* write the rest non word aligned bytes */ + if (bytes) { + tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000); + if (tx_level < 0) + return tx_level; + memcpy(&tmp, buf, bytes); + writel(tmp, sfc->regbase + SFC_DATA); + } + + return len; +} + +static int rockchip_sfc_read_fifo(struct rockchip_sfc *sfc, u8 *buf, int len) +{ + u8 bytes = len & 0x3; + u32 dwords; + u8 read_words; + int rx_level; + int tmp; + + /* word aligned access only */ + dwords = len >> 2; + while (dwords) { + rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000); + if (rx_level < 0) + return rx_level; + read_words = min_t(u32, rx_level, dwords); + ioread32_rep(sfc->regbase + SFC_DATA, buf, read_words); + buf += read_words << 2; + dwords -= read_words; + } + + /* read the rest non word aligned bytes */ + if (bytes) { + rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000); + if (rx_level < 0) + return rx_level; + tmp = readl(sfc->regbase + SFC_DATA); + memcpy(buf, &tmp, bytes); + } + + return len; +} + +static int rockchip_sfc_fifo_transfer_dma(struct rockchip_sfc *sfc, dma_addr_t dma_buf, size_t len) +{ + writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR); + writel((u32)dma_buf, sfc->regbase + SFC_DMA_ADDR); + writel(SFC_DMA_TRIGGER_START, sfc->regbase + SFC_DMA_TRIGGER); + + return len; +} + +static int rockchip_sfc_xfer_data_poll(struct rockchip_sfc *sfc, + const struct spi_mem_op *op, u32 len) +{ + dev_dbg(sfc->dev, "sfc xfer_poll len=%x\n", len); + + if (op->data.dir == SPI_MEM_DATA_OUT) + return rockchip_sfc_write_fifo(sfc, op->data.buf.out, len); + else + return rockchip_sfc_read_fifo(sfc, op->data.buf.in, len); +} + +static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc, + const struct spi_mem_op *op, u32 len) +{ + int ret; + + dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len); + + if (op->data.dir == SPI_MEM_DATA_OUT) + memcpy_toio(sfc->buffer, op->data.buf.out, len); + + ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len); + if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) { + dev_err(sfc->dev, "DMA wait for transfer finish timeout\n"); + ret = -ETIMEDOUT; + } + rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA); + if (op->data.dir == SPI_MEM_DATA_IN) + memcpy_fromio(op->data.buf.in, sfc->buffer, len); + + return ret; +} + +static int rockchip_sfc_xfer_done(struct rockchip_sfc *sfc, u32 timeout_us) +{ + int ret = 0; + u32 status; + + ret = readl_poll_timeout(sfc->regbase + SFC_SR, status, + !(status & SFC_SR_IS_BUSY), + 20, timeout_us); + if (ret) { + dev_err(sfc->dev, "wait sfc idle timeout\n"); + rockchip_sfc_reset(sfc); + + ret = -EIO; + } + + return ret; +} + +static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct rockchip_sfc *sfc = spi_master_get_devdata(mem->spi->master); + u32 len = op->data.nbytes; + int ret; + + if (unlikely(mem->spi->max_speed_hz != sfc->frequency)) { + ret = clk_set_rate(sfc->clk, mem->spi->max_speed_hz); + if (ret) + return ret; + sfc->frequency = mem->spi->max_speed_hz; + dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n", + sfc->frequency, clk_get_rate(sfc->clk)); + } + + rockchip_sfc_adjust_op_work((struct spi_mem_op *)op); + rockchip_sfc_xfer_setup(sfc, mem, op, len); + if (len) { + if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD) { + init_completion(&sfc->cp); + rockchip_sfc_irq_unmask(sfc, SFC_IMR_DMA); + ret = rockchip_sfc_xfer_data_dma(sfc, op, len); + } else { + ret = rockchip_sfc_xfer_data_poll(sfc, op, len); + } + + if (ret != len) { + dev_err(sfc->dev, "xfer data failed ret %d dir %d\n", ret, op->data.dir); + + return -EIO; + } + } + + return rockchip_sfc_xfer_done(sfc, 100000); +} + +static int rockchip_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + struct rockchip_sfc *sfc = spi_master_get_devdata(mem->spi->master); + + op->data.nbytes = min(op->data.nbytes, sfc->max_iosize); + + return 0; +} + +static const struct spi_controller_mem_ops rockchip_sfc_mem_ops = { + .exec_op = rockchip_sfc_exec_mem_op, + .adjust_op_size = rockchip_sfc_adjust_op_size, +}; + +static irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id) +{ + struct rockchip_sfc *sfc = dev_id; + u32 reg; + + reg = readl(sfc->regbase + SFC_RISR); + + /* Clear interrupt */ + writel_relaxed(reg, sfc->regbase + SFC_ICLR); + + if (reg & SFC_RISR_DMA) { + complete(&sfc->cp); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int rockchip_sfc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_master *master; + struct resource *res; + struct rockchip_sfc *sfc; + int ret; + + master = devm_spi_alloc_master(&pdev->dev, sizeof(*sfc)); + if (!master) + return -ENOMEM; + + master->flags = SPI_MASTER_HALF_DUPLEX; + master->mem_ops = &rockchip_sfc_mem_ops; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL; + master->max_speed_hz = SFC_MAX_SPEED; + master->num_chipselect = SFC_MAX_CHIPSELECT_NUM; + + sfc = spi_master_get_devdata(master); + sfc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sfc->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(sfc->regbase)) + return PTR_ERR(sfc->regbase); + + sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc"); + if (IS_ERR(sfc->clk)) { + dev_err(&pdev->dev, "Failed to get sfc interface clk\n"); + return PTR_ERR(sfc->clk); + } + + sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc"); + if (IS_ERR(sfc->hclk)) { + dev_err(&pdev->dev, "Failed to get sfc ahb clk\n"); + return PTR_ERR(sfc->hclk); + } + + sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, + "rockchip,sfc-no-dma"); + + if (sfc->use_dma) { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_warn(dev, "Unable to set dma mask\n"); + return ret; + } + + sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3, + &sfc->dma_buffer, + GFP_KERNEL); + if (!sfc->buffer) + return -ENOMEM; + } + + ret = clk_prepare_enable(sfc->hclk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable ahb clk\n"); + goto err_hclk; + } + + ret = clk_prepare_enable(sfc->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable interface clk\n"); + goto err_clk; + } + + /* Find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "Failed to get the irq\n"); + goto err_irq; + } + + ret = devm_request_irq(dev, ret, rockchip_sfc_irq_handler, + 0, pdev->name, sfc); + if (ret) { + dev_err(dev, "Failed to request irq\n"); + + return ret; + } + + ret = rockchip_sfc_init(sfc); + if (ret) + goto err_irq; + + sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc); + sfc->version = rockchip_sfc_get_version(sfc); + + ret = spi_register_master(master); + if (ret) + goto err_irq; + + return 0; + +err_irq: + clk_disable_unprepare(sfc->clk); +err_clk: + clk_disable_unprepare(sfc->hclk); +err_hclk: + return ret; +} + +static int rockchip_sfc_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct rockchip_sfc *sfc = platform_get_drvdata(pdev); + + spi_unregister_master(master); + + clk_disable_unprepare(sfc->clk); + clk_disable_unprepare(sfc->hclk); + + return 0; +} + +static const struct of_device_id rockchip_sfc_dt_ids[] = { + { .compatible = "rockchip,sfc"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rockchip_sfc_dt_ids); + +static struct platform_driver rockchip_sfc_driver = { + .driver = { + .name = "rockchip-sfc", + .of_match_table = rockchip_sfc_dt_ids, + }, + .probe = rockchip_sfc_probe, + .remove = rockchip_sfc_remove, +}; +module_platform_driver(rockchip_sfc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Rockchip Serial Flash Controller Driver"); +MODULE_AUTHOR("Shawn Lin "); +MODULE_AUTHOR("Chris Morgan "); +MODULE_AUTHOR("Jon Lin "); From 1e4f30eaf4b81252bbd462cf7e95a08959f5bebd Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 16 Aug 2021 18:50:13 +0200 Subject: [PATCH 150/177] power: supply: max17042_battery: clean up MAX17055_V_empty This register is same as in MAX17047 and MAX17050, so there's no need for custom casing it. Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 4 ---- include/linux/power/max17042_battery.h | 1 - 2 files changed, 5 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index c6078f179fb3..01e6728a9e2b 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -283,8 +283,6 @@ static int max17042_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ret = regmap_read(map, MAX17042_V_empty, &data); - else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) - ret = regmap_read(map, MAX17055_V_empty, &data); else ret = regmap_read(map, MAX17047_V_empty, &data); if (ret < 0) @@ -778,8 +776,6 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) max17042_override_por(map, MAX17042_V_empty, config->vempty); - if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) - max17042_override_por(map, MAX17055_V_empty, config->vempty); else max17042_override_por(map, MAX17047_V_empty, config->vempty); max17042_override_por(map, MAX17042_TempNom, config->temp_nom); diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index d55c746ac56e..7e5da60cbea3 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -113,7 +113,6 @@ enum max17042_register { enum max17055_register { MAX17055_QRes = 0x0C, MAX17055_TTF = 0x20, - MAX17055_V_empty = 0x3A, MAX17055_TIMER = 0x3E, MAX17055_USER_MEM = 0x40, MAX17055_RGAIN = 0x42, From ed0d0a0506025f06061325cedae1bbebd081620a Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 16 Aug 2021 18:50:14 +0200 Subject: [PATCH 151/177] power: supply: max17042_battery: fix typo in MAx17042_TOFF Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 2 +- include/linux/power/max17042_battery.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 01e6728a9e2b..eeccc365fe54 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -744,7 +744,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) struct max17042_config_data *config = chip->pdata->config_data; max17042_override_por(map, MAX17042_TGAIN, config->tgain); - max17042_override_por(map, MAx17042_TOFF, config->toff); + max17042_override_por(map, MAX17042_TOFF, config->toff); max17042_override_por(map, MAX17042_CGAIN, config->cgain); max17042_override_por(map, MAX17042_COFF, config->coff); diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 7e5da60cbea3..86f16fc3b694 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -69,7 +69,7 @@ enum max17042_register { MAX17042_RelaxCFG = 0x2A, MAX17042_MiscCFG = 0x2B, MAX17042_TGAIN = 0x2C, - MAx17042_TOFF = 0x2D, + MAX17042_TOFF = 0x2D, MAX17042_CGAIN = 0x2E, MAX17042_COFF = 0x2F, From 4bf00434a6183a33c1fa315db7cc4d4a00a76be0 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 16 Aug 2021 18:50:15 +0200 Subject: [PATCH 152/177] power: supply: max17042_battery: more robust chip type checks Prepared by checking the datasheets of max17042, max17047/50 and max170455 for differences in register maps. Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 40 +++++++++++++------------ include/linux/power/max17042_battery.h | 9 ++++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index eeccc365fe54..e6bcda966476 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -763,34 +763,36 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg); max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg); max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg); - max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); max17042_override_por(map, MAX17042_FullCAP, config->fullcap); max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom); - if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) - max17042_override_por(map, MAX17042_SOC_empty, - config->socempty); - max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); max17042_override_por(map, MAX17042_dQacc, config->dqacc); max17042_override_por(map, MAX17042_dPacc, config->dpacc); - if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) - max17042_override_por(map, MAX17042_V_empty, config->vempty); - else - max17042_override_por(map, MAX17047_V_empty, config->vempty); - max17042_override_por(map, MAX17042_TempNom, config->temp_nom); - max17042_override_por(map, MAX17042_TempLim, config->temp_lim); - max17042_override_por(map, MAX17042_FCTC, config->fctc); max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); max17042_override_por(map, MAX17042_TempCo, config->tcompc0); - if (chip->chip_type && - ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) || + + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { + max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); + max17042_override_por(map, MAX17042_SOC_empty, config->socempty); + max17042_override_por(map, MAX17042_V_empty, config->vempty); + max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco); + max17042_override_por(map, MAX17042_K_empty0, config->kempty0); + } + + if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) || (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) { - max17042_override_por(map, MAX17042_EmptyTempCo, - config->empty_tempco); - max17042_override_por(map, MAX17042_K_empty0, - config->kempty0); + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) { + max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); + max17042_override_por(map, MAX17042_TempNom, config->temp_nom); + max17042_override_por(map, MAX17042_TempLim, config->temp_lim); + max17042_override_por(map, MAX17042_FCTC, config->fctc); + } + + if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) { + max17042_override_por(map, MAX17047_V_empty, config->vempty); } } diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 86f16fc3b694..dd24756a8af7 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -110,12 +110,14 @@ enum max17042_register { MAX17042_VFSOC = 0xFF, }; +/* Registers specific to max17055 only */ enum max17055_register { MAX17055_QRes = 0x0C, + MAX17055_RCell = 0x14, MAX17055_TTF = 0x20, - MAX17055_TIMER = 0x3E, + MAX17055_DieTemp = 0x34, MAX17055_USER_MEM = 0x40, - MAX17055_RGAIN = 0x42, + MAX17055_RGAIN = 0x43, MAX17055_ConvgCfg = 0x49, MAX17055_VFRemCap = 0x4A, @@ -154,13 +156,14 @@ enum max17055_register { MAX17055_AtAvCap = 0xDF, }; -/* Registers specific to max17047/50 */ +/* Registers specific to max17047/50/55 */ enum max17047_register { MAX17047_QRTbl00 = 0x12, MAX17047_FullSOCThr = 0x13, MAX17047_QRTbl10 = 0x22, MAX17047_QRTbl20 = 0x32, MAX17047_V_empty = 0x3A, + MAX17047_TIMER = 0x3E, MAX17047_QRTbl30 = 0x42, }; From eaa2c490514d2d49c3ef1764530234d07f422289 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 16 Aug 2021 18:50:16 +0200 Subject: [PATCH 153/177] power: supply: max17042_battery: log SOC threshold using debug log level There's no need to print a message on every change in battery percentage on regular log levels. Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index e6bcda966476..8dffae76b6a3 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -872,7 +872,7 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) return IRQ_HANDLED; if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) { - dev_info(&chip->client->dev, "SOC threshold INTR\n"); + dev_dbg(&chip->client->dev, "SOC threshold INTR\n"); max17042_set_soc_threshold(chip, 1); } From c049742fbc71129c481a6d5e52392b9aa482cc9e Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 18 Aug 2021 07:18:19 +0300 Subject: [PATCH 154/177] regulator: Minor regulator documentation fixes. The newly added regulator ramp-delay specifiers in regulator desc lacked the documentation. Add some. Also fix a typo. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/20210818041513.GA2408290@dc7vkhyh15000m40t6jht-3.rev.dnainternet.fi Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 5447a6b33fa0..92bf7584a2f0 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -337,6 +337,12 @@ enum regulator_type { * @pull_down_val_on: Enabling value for control when using regmap * set_pull_down * + * @ramp_reg: Register for controlling the regulator ramp-rate. + * @ramp_mask: Bitmask for the ramp-rate control register. + * @ramp_delay_table: Table for mapping the regulator ramp-rate values. Values + * should be given in units of V/S (uV/uS). See the + * regulator_set_ramp_delay_regmap(). + * * @enable_time: Time taken for initial enable of regulator (in uS). * @off_on_delay: guard time (in uS), before re-enabling a regulator * @@ -462,7 +468,7 @@ struct regulator_err_state { }; /** - * struct regulator_irq_data - regulator error/notification status date + * struct regulator_irq_data - regulator error/notification status data * * @states: Status structs for each of the associated regulators. * @num_states: Amount of associated regulators. From 02cea7039ad52593ee05824c19233366914df9b2 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 10 Aug 2021 22:22:30 +0800 Subject: [PATCH 155/177] spi: tegra20-slink: remove spi_master_put() in tegra_slink_remove() spi_master_put() is already called in spi_unregister_master(), or it will lead a double decrement refcount. Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20210810142230.2220453-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index deff16ba6d58..ebd27f883033 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1154,8 +1154,6 @@ static int tegra_slink_remove(struct platform_device *pdev) if (tspi->rx_dma_chan) tegra_slink_deinit_dma_param(tspi, true); - spi_master_put(master); - return 0; } From d68f4c73d729245a47e70eb216fa24bc174ed2e2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 18 Aug 2021 22:55:56 +0200 Subject: [PATCH 156/177] spi: coldfire-qspi: Use clk_disable_unprepare in the remove function 'clk_prepare_enable()' is used in the probe, so 'clk_disable_unprepare()' should be used in the remove function to be consistent. Fixes: 499de01c5c0b ("spi: coldfire-qspi: Use clk_prepare_enable and clk_disable_unprepare") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/ee91792ddba61342b0d3284cd4558a2b0016c4e7.1629319838.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-coldfire-qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 8996115ce736..263ce9047327 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -444,7 +444,7 @@ static int mcfqspi_remove(struct platform_device *pdev) mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); mcfqspi_cs_teardown(mcfqspi); - clk_disable(mcfqspi->clk); + clk_disable_unprepare(mcfqspi->clk); return 0; } From 7a4697b201a617907e4b440ae34df601d4755bef Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Wed, 14 Jul 2021 03:10:04 +0800 Subject: [PATCH 157/177] spi: stm32: fix excluded_middle.cocci warnings drivers/spi/spi-stm32.c:915:23-25: WARNING !A || A && B is equivalent to !A || B Condition !A || A && B is equivalent to !A || B. Generated by: scripts/coccinelle/misc/excluded_middle.cocci Fixes: 7ceb0b8a3ced ("spi: stm32: finalize message either on dma callback or EOT") CC: Alain Volmat Reported-by: kernel test robot Signed-off-by: kernel test robot Reviewed-by: Alain Volmat Link: https://lore.kernel.org/r/20210713191004.GA14729@5eb5c2cbef84 Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 14ca7ea04e47..9bd3fd1652f7 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -912,8 +912,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi); if (!spi->cur_usedma || - (spi->cur_usedma && (spi->cur_comm == SPI_SIMPLEX_TX || - spi->cur_comm == SPI_3WIRE_TX))) + (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)) end = true; } From c9398455b046fc7a44b6dd53d9d6fe4b11c21700 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 20 Aug 2021 02:51:11 +0300 Subject: [PATCH 158/177] power: supply: core: Fix parsing of battery chemistry/technology The power_supply_get_battery_info() fails if device-chemistry property is missing in a device-tree because error variable is propagated to the final return of the function, fix it. Fixes: 4eef766b7d4d ("power: supply: core: Parse battery chemistry/technology") Signed-off-by: Dmitry Osipenko Reviewed-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index dd62c871b2b5..0c2132c7f5d4 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -619,8 +619,7 @@ int power_supply_get_battery_info(struct power_supply *psy, * Documentation/power/power_supply_class.rst. */ - err = of_property_read_string(battery_np, "device-chemistry", &value); - if (!err) { + if (!of_property_read_string(battery_np, "device-chemistry", &value)) { if (!strcmp("nickel-cadmium", value)) info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd; else if (!strcmp("nickel-metal-hydride", value)) From ad3ead1efe057029bf112e13d7ef5901915d6abd Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Mon, 23 Aug 2021 10:56:51 +0300 Subject: [PATCH 159/177] regulator: Documentation fix for regulator error notification helper The helper to send IRQ notification for regulator errors had still old description mentioning calling BUG() as a last resort when error status reading has kept failing for more times than a given threshold. The impementation calling BUG() did never end-up in-tree but was replaced by hopefully more sophisticated handler trying to power-off the system. Fix the documentation to reflect actual behaviour. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/20210823075651.GA3717293@localhost.localdomain Signed-off-by: Mark Brown --- drivers/regulator/irq_helpers.c | 2 +- include/linux/regulator/driver.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/irq_helpers.c b/drivers/regulator/irq_helpers.c index fabe2e53093e..522764435575 100644 --- a/drivers/regulator/irq_helpers.c +++ b/drivers/regulator/irq_helpers.c @@ -184,7 +184,7 @@ static irqreturn_t regulator_notifier_isr(int irq, void *data) * If retry_count exceeds the given safety limit we call IC specific die * handler which can try disabling regulator(s). * - * If no die handler is given we will just bug() as a last resort. + * If no die handler is given we will just power-off as a last resort. * * We could try disabling all associated rdevs - but we might shoot * ourselves in the head and leave the problematic regulator enabled. So diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 92bf7584a2f0..bd7a73db2e66 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -527,8 +527,8 @@ struct regulator_irq_data { * active events as core does not clean the map data. * REGULATOR_FAILED_RETRY can be returned to indicate that the * status reading from IC failed. If this is repeated for - * fatal_cnt times the core will call die() callback or BUG() - * as a last resort to protect the HW. + * fatal_cnt times the core will call die() callback or power-off + * the system as a last resort to protect the HW. * @renable: Optional callback to check status (if HW supports that) before * re-enabling IRQ. If implemented this should clear the error * flags so that errors fetched by regulator_get_error_flags() @@ -537,7 +537,8 @@ struct regulator_irq_data { * REGULATOR_FAILED_RETRY can be returned to * indicate that the status reading from IC failed. If this is * repeated for 'fatal_cnt' times the core will call die() - * callback or BUG() as a last resort to protect the HW. + * callback or if die() is not populated then attempt to power-off + * the system as a last resort to protect the HW. * Returning zero indicates that the problem in HW has been solved * and IRQ will be re-enabled. Returning REGULATOR_ERROR_ON * indicates the error condition is still active and keeps IRQ From 58bc6d1be2f3b0ceecb6027dfa17513ec6aa2abb Mon Sep 17 00:00:00 2001 From: Stian Skjelstad Date: Sun, 22 Aug 2021 11:33:32 +0200 Subject: [PATCH 160/177] udf_get_extendedattr() had no boundary checks. When parsing the ExtendedAttr data, malicous or corrupt attribute length could cause kernel hangs and buffer overruns in some special cases. Link: https://lore.kernel.org/r/20210822093332.25234-1-stian.skjelstad@gmail.com Signed-off-by: Stian Skjelstad Signed-off-by: Jan Kara --- fs/udf/misc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/udf/misc.c b/fs/udf/misc.c index eab94527340d..1614d308d0f0 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -173,13 +173,22 @@ struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, else offset = le32_to_cpu(eahd->appAttrLocation); - while (offset < iinfo->i_lenEAttr) { + while (offset + sizeof(*gaf) < iinfo->i_lenEAttr) { + uint32_t attrLength; + gaf = (struct genericFormat *)&ea[offset]; + attrLength = le32_to_cpu(gaf->attrLength); + + /* Detect undersized elements and buffer overflows */ + if ((attrLength < sizeof(*gaf)) || + (attrLength > (iinfo->i_lenEAttr - offset))) + break; + if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) return gaf; else - offset += le32_to_cpu(gaf->attrLength); + offset += attrLength; } } From 8d00f9819458b95301e274c6df705df2963ba34f Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sat, 21 Aug 2021 20:49:25 +0800 Subject: [PATCH 161/177] spi: rockchip-sfc: Remove redundant IO operations Coherent dma buffer is uncached and memcpy is enough. Signed-off-by: Jon Lin Link: https://lore.kernel.org/r/20210821124925.6066-1-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip-sfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 7c4d47fe80c2..81154a8836fc 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -453,7 +453,7 @@ static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc, dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len); if (op->data.dir == SPI_MEM_DATA_OUT) - memcpy_toio(sfc->buffer, op->data.buf.out, len); + memcpy(sfc->buffer, op->data.buf.out, len); ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len); if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) { @@ -462,7 +462,7 @@ static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc, } rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA); if (op->data.dir == SPI_MEM_DATA_IN) - memcpy_fromio(op->data.buf.in, sfc->buffer, len); + memcpy(op->data.buf.in, sfc->buffer, len); return ret; } From 745649c59a0d1fde9dcc02286f23f8c78a1f724d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 18 Aug 2021 15:10:51 +0100 Subject: [PATCH 162/177] spi: rockchip-sfc: Fix assigned but never used return error codes Currently there are two places where the error return variable ret is being assigned -ETIMEDOUT on timeout errors and this value is not being returned. Fix this by returning -ETIMEDOUT rather than redundantly assiging it to ret. Addresses-Coverity: ("Unused value") Fixes: 0b89fc0a367e ("spi: rockchip-sfc: add rockchip serial flash controller") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20210818141051.36320-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip-sfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 81154a8836fc..a46b38544027 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -257,7 +257,7 @@ static int rockchip_sfc_wait_txfifo_ready(struct rockchip_sfc *sfc, u32 timeout_ if (ret) { dev_dbg(sfc->dev, "sfc wait tx fifo timeout\n"); - ret = -ETIMEDOUT; + return -ETIMEDOUT; } return (status & SFC_FSR_TXLV_MASK) >> SFC_FSR_TXLV_SHIFT; @@ -274,7 +274,7 @@ static int rockchip_sfc_wait_rxfifo_ready(struct rockchip_sfc *sfc, u32 timeout_ if (ret) { dev_dbg(sfc->dev, "sfc wait rx fifo timeout\n"); - ret = -ETIMEDOUT; + return -ETIMEDOUT; } return (status & SFC_FSR_RXLV_MASK) >> SFC_FSR_RXLV_SHIFT; From 5dc349ec131c6d40aeb2545064e285f0025fbb39 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Tue, 24 Aug 2021 15:02:10 +0800 Subject: [PATCH 163/177] spi: sprd: Pass offset instead of physical address to adi_read/_write() The register offset would be added a physical address base and then pass to the function sprd_adt_read()/_write() each time before calling them. So we can do that within these two functions instead, that would make the code more clear. Signed-off-by: Chunyan Zhang Link: https://lore.kernel.org/r/20210824070212.2089255-1-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 105 ++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 61 deletions(-) diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index ab19068be867..abdad1ea7b38 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -117,24 +117,18 @@ struct sprd_adi { struct notifier_block restart_handler; }; -static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr) +static int sprd_adi_check_addr(struct sprd_adi *sadi, u32 reg) { - if (paddr < sadi->slave_pbase || paddr > - (sadi->slave_pbase + ADI_SLAVE_ADDR_SIZE)) { + if (reg > ADI_SLAVE_ADDR_SIZE) { dev_err(sadi->dev, - "slave physical address is incorrect, addr = 0x%x\n", - paddr); + "slave address offset is incorrect, reg = 0x%x\n", + reg); return -EINVAL; } return 0; } -static unsigned long sprd_adi_to_vaddr(struct sprd_adi *sadi, u32 paddr) -{ - return (paddr - sadi->slave_pbase + sadi->slave_vbase); -} - static int sprd_adi_drain_fifo(struct sprd_adi *sadi) { u32 timeout = ADI_FIFO_DRAIN_TIMEOUT; @@ -161,11 +155,11 @@ static int sprd_adi_fifo_is_full(struct sprd_adi *sadi) return readl_relaxed(sadi->base + REG_ADI_ARM_FIFO_STS) & BIT_FIFO_FULL; } -static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val) +static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) { int read_timeout = ADI_READ_TIMEOUT; unsigned long flags; - u32 val, rd_addr; + u32 val, rd_addr, paddr; int ret = 0; if (sadi->hwlock) { @@ -178,11 +172,16 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val) } } + ret = sprd_adi_check_addr(sadi, reg); + if (ret) + goto out; + /* * Set the physical register address need to read into RD_CMD register, * then ADI controller will start to transfer automatically. */ - writel_relaxed(reg_paddr, sadi->base + REG_ADI_RD_CMD); + paddr = sadi->slave_pbase + reg; + writel_relaxed(paddr, sadi->base + REG_ADI_RD_CMD); /* * Wait read operation complete, the BIT_RD_CMD_BUSY will be set @@ -212,9 +211,9 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val) */ rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT; - if (rd_addr != (reg_paddr & REG_ADDR_LOW_MASK)) { + if (rd_addr != (paddr & REG_ADDR_LOW_MASK)) { dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n", - reg_paddr, val); + paddr, val); ret = -EIO; goto out; } @@ -227,9 +226,8 @@ out: return ret; } -static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val) +static int sprd_adi_write(struct sprd_adi *sadi, u32 reg, u32 val) { - unsigned long reg = sprd_adi_to_vaddr(sadi, reg_paddr); u32 timeout = ADI_FIFO_DRAIN_TIMEOUT; unsigned long flags; int ret; @@ -244,6 +242,10 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val) } } + ret = sprd_adi_check_addr(sadi, reg); + if (ret) + goto out; + ret = sprd_adi_drain_fifo(sadi); if (ret < 0) goto out; @@ -254,7 +256,8 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val) */ do { if (!sprd_adi_fifo_is_full(sadi)) { - writel_relaxed(val, (void __iomem *)reg); + /* we need virtual register address to write. */ + writel_relaxed(val, (void __iomem *)(sadi->slave_vbase + reg)); break; } @@ -277,44 +280,24 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, struct spi_transfer *t) { struct sprd_adi *sadi = spi_controller_get_devdata(ctlr); - u32 phy_reg, val; + u32 reg, val; int ret; if (t->rx_buf) { - phy_reg = *(u32 *)t->rx_buf + sadi->slave_pbase; - - ret = sprd_adi_check_paddr(sadi, phy_reg); - if (ret) - return ret; - - ret = sprd_adi_read(sadi, phy_reg, &val); - if (ret) - return ret; - + reg = *(u32 *)t->rx_buf; + ret = sprd_adi_read(sadi, reg, &val); *(u32 *)t->rx_buf = val; } else if (t->tx_buf) { u32 *p = (u32 *)t->tx_buf; - - /* - * Get the physical register address need to write and convert - * the physical address to virtual address. Since we need - * virtual register address to write. - */ - phy_reg = *p++ + sadi->slave_pbase; - ret = sprd_adi_check_paddr(sadi, phy_reg); - if (ret) - return ret; - + reg = *p++; val = *p; - ret = sprd_adi_write(sadi, phy_reg, val); - if (ret) - return ret; + ret = sprd_adi_write(sadi, reg, val); } else { dev_err(sadi->dev, "no buffer for transfer\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; + return ret; } static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) @@ -323,9 +306,9 @@ static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) u32 val; /* Set default watchdog reboot mode */ - sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val); + sprd_adi_read(sadi, PMIC_RST_STATUS, &val); val |= HWRST_STATUS_WATCHDOG; - sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val); + sprd_adi_write(sadi, PMIC_RST_STATUS, val); #endif } @@ -366,40 +349,40 @@ static int sprd_adi_restart_handler(struct notifier_block *this, reboot_mode = HWRST_STATUS_NORMAL; /* Record the reboot mode */ - sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val); + sprd_adi_read(sadi, PMIC_RST_STATUS, &val); val &= ~HWRST_STATUS_WATCHDOG; val |= reboot_mode; - sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val); + sprd_adi_write(sadi, PMIC_RST_STATUS, val); /* Enable the interface clock of the watchdog */ - sprd_adi_read(sadi, sadi->slave_pbase + PMIC_MODULE_EN, &val); + sprd_adi_read(sadi, PMIC_MODULE_EN, &val); val |= BIT_WDG_EN; - sprd_adi_write(sadi, sadi->slave_pbase + PMIC_MODULE_EN, val); + sprd_adi_write(sadi, PMIC_MODULE_EN, val); /* Enable the work clock of the watchdog */ - sprd_adi_read(sadi, sadi->slave_pbase + PMIC_CLK_EN, &val); + sprd_adi_read(sadi, PMIC_CLK_EN, &val); val |= BIT_WDG_EN; - sprd_adi_write(sadi, sadi->slave_pbase + PMIC_CLK_EN, val); + sprd_adi_write(sadi, PMIC_CLK_EN, val); /* Unlock the watchdog */ - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, WDG_UNLOCK_KEY); + sprd_adi_write(sadi, REG_WDG_LOCK, WDG_UNLOCK_KEY); - sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val); + sprd_adi_read(sadi, REG_WDG_CTRL, &val); val |= BIT_WDG_NEW; - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val); + sprd_adi_write(sadi, REG_WDG_CTRL, val); /* Load the watchdog timeout value, 50ms is always enough. */ - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_HIGH, 0); - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_LOW, + sprd_adi_write(sadi, REG_WDG_LOAD_HIGH, 0); + sprd_adi_write(sadi, REG_WDG_LOAD_LOW, WDG_LOAD_VAL & WDG_LOAD_MASK); /* Start the watchdog to reset system */ - sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val); + sprd_adi_read(sadi, REG_WDG_CTRL, &val); val |= BIT_WDG_RUN | BIT_WDG_RST; - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val); + sprd_adi_write(sadi, REG_WDG_CTRL, val); /* Lock the watchdog */ - sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, ~WDG_UNLOCK_KEY); + sprd_adi_write(sadi, REG_WDG_LOCK, ~WDG_UNLOCK_KEY); mdelay(1000); From 2b961c51f4d35c45116b21936b563cbb78fba540 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Tue, 24 Aug 2021 15:02:11 +0800 Subject: [PATCH 164/177] spi: sprd: Make sure offset not equal to slave address size The slave register offset shouldn't equal to the max slave address which ADI can support to access. Signed-off-by: Chunyan Zhang Link: https://lore.kernel.org/r/20210824070212.2089255-2-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index abdad1ea7b38..06af519c0b21 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -119,7 +119,7 @@ struct sprd_adi { static int sprd_adi_check_addr(struct sprd_adi *sadi, u32 reg) { - if (reg > ADI_SLAVE_ADDR_SIZE) { + if (reg >= ADI_SLAVE_ADDR_SIZE) { dev_err(sadi->dev, "slave address offset is incorrect, reg = 0x%x\n", reg); From f674aacd5005184acf3cf7b851a299573d64fdd6 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Tue, 24 Aug 2021 15:02:12 +0800 Subject: [PATCH 165/177] spi: sprd: fill offset only to RD_CMD register for reading from slave device RD_CMD can accept slave address offset only, higher bits are reserved. Writing the whole slave address including slave base seems unnecessary. Signed-off-by: Chunyan Zhang Link: https://lore.kernel.org/r/20210824070212.2089255-3-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 06af519c0b21..07f11b17bf20 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -159,7 +159,7 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) { int read_timeout = ADI_READ_TIMEOUT; unsigned long flags; - u32 val, rd_addr, paddr; + u32 val, rd_addr; int ret = 0; if (sadi->hwlock) { @@ -177,11 +177,10 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) goto out; /* - * Set the physical register address need to read into RD_CMD register, + * Set the slave address offset need to read into RD_CMD register, * then ADI controller will start to transfer automatically. */ - paddr = sadi->slave_pbase + reg; - writel_relaxed(paddr, sadi->base + REG_ADI_RD_CMD); + writel_relaxed(reg, sadi->base + REG_ADI_RD_CMD); /* * Wait read operation complete, the BIT_RD_CMD_BUSY will be set @@ -211,9 +210,9 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) */ rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT; - if (rd_addr != (paddr & REG_ADDR_LOW_MASK)) { + if (rd_addr != (reg & REG_ADDR_LOW_MASK)) { dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n", - paddr, val); + reg, val); ret = -EIO; goto out; } From ea4ab99cb58cc9f8d64c0961ff9a059825f304cf Mon Sep 17 00:00:00 2001 From: Matija Glavinic Pecotic Date: Tue, 24 Aug 2021 11:25:56 +0200 Subject: [PATCH 166/177] spi: davinci: invoke chipselect callback Davinci needs to configure chipselect on transfer. Fixes: 4a07b8bcd503 ("spi: bitbang: Make chipselect callback optional") Signed-off-by: Matija Glavinic Pecotic Reviewed-by: Alexander Sverdlin Link: https://lore.kernel.org/r/735fb7b0-82aa-5b9b-85e4-53f0c348cc0e@nokia.com Signed-off-by: Mark Brown --- drivers/spi/spi-davinci.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index e114e6fe5ea5..d112c2cac042 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -213,12 +213,6 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * line for the controller */ if (spi->cs_gpiod) { - /* - * FIXME: is this code ever executed? This host does not - * set SPI_MASTER_GPIO_SS so this chipselect callback should - * not get called from the SPI core when we are using - * GPIOs for chip select. - */ if (value == BITBANG_CS_ACTIVE) gpiod_set_value(spi->cs_gpiod, 1); else @@ -945,7 +939,7 @@ static int davinci_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16); - master->flags = SPI_MASTER_MUST_RX; + master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_GPIO_SS; master->setup = davinci_spi_setup; master->cleanup = davinci_spi_cleanup; master->can_dma = davinci_spi_can_dma; From 98e47570ba985f2310586c80409238200fa3170f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 25 Aug 2021 11:37:03 +0800 Subject: [PATCH 167/177] regulator: vctrl: Use locked regulator_get_voltage in probe path In commit e9153311491d ("regulator: vctrl-regulator: Avoid deadlock getting and setting the voltage"), all calls to get/set the voltage of the control regulator were switched to unlocked versions to avoid deadlocks. However, the call in the probe path is done without regulator locks held. In this case the locked version should be used. Switch back to the locked regulator_get_voltage() in the probe path to avoid any mishaps. Fixes: e9153311491d ("regulator: vctrl-regulator: Avoid deadlock getting and setting the voltage") Signed-off-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20210825033704.3307263-2-wenst@chromium.org Signed-off-by: Mark Brown --- drivers/regulator/vctrl-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c index cbadb1c99679..93d33201ffe0 100644 --- a/drivers/regulator/vctrl-regulator.c +++ b/drivers/regulator/vctrl-regulator.c @@ -490,7 +490,8 @@ static int vctrl_probe(struct platform_device *pdev) if (ret) return ret; - ctrl_uV = regulator_get_voltage_rdev(vctrl->ctrl_reg->rdev); + /* Use locked consumer API when not in regulator framework */ + ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg); if (ctrl_uV < 0) { dev_err(&pdev->dev, "failed to get control voltage\n"); return ctrl_uV; From 21e39809fd7c4b8ff3662f23e0168e87594c8ca8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 25 Aug 2021 11:37:04 +0800 Subject: [PATCH 168/177] regulator: vctrl: Avoid lockdep warning in enable/disable ops vctrl_enable() and vctrl_disable() call regulator_enable() and regulator_disable(), respectively. However, vctrl_* are regulator ops and should not be calling the locked regulator APIs. Doing so results in a lockdep warning. Instead of exporting more internal regulator ops, model the ctrl supply as an actual supply to vctrl-regulator. At probe time this driver still needs to use the consumer API to fetch its constraints, but otherwise lets the regulator core handle the upstream supply for it. The enable/disable/is_enabled ops are not removed, but now only track state internally. This preserves the original behavior with the ops being available, but one could argue that the original behavior was already incorrect: the internal state would not match the upstream supply if that supply had another consumer that enabled the supply, while vctrl-regulator was not enabled. The lockdep warning is as follows: WARNING: possible circular locking dependency detected 5.14.0-rc6 #2 Not tainted ------------------------------------------------------ swapper/0/1 is trying to acquire lock: ffffffc011306d00 (regulator_list_mutex){+.+.}-{3:3}, at: regulator_lock_dependent (arch/arm64/include/asm/current.h:19 include/linux/ww_mutex.h:111 drivers/regulator/core.c:329) but task is already holding lock: ffffff8004a77160 (regulator_ww_class_mutex){+.+.}-{3:3}, at: regulator_lock_recursive (drivers/regulator/core.c:156 drivers/regulator/core.c:263) which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (regulator_ww_class_mutex){+.+.}-{3:3}: __mutex_lock_common (include/asm-generic/atomic-instrumented.h:606 include/asm-generic/atomic-long.h:29 kernel/locking/mutex.c:103 kernel/locking/mutex.c:144 kernel/locking/mutex.c:963) ww_mutex_lock (kernel/locking/mutex.c:1199) regulator_lock_recursive (drivers/regulator/core.c:156 drivers/regulator/core.c:263) regulator_lock_dependent (drivers/regulator/core.c:343) regulator_enable (drivers/regulator/core.c:2808) set_machine_constraints (drivers/regulator/core.c:1536) regulator_register (drivers/regulator/core.c:5486) devm_regulator_register (drivers/regulator/devres.c:196) reg_fixed_voltage_probe (drivers/regulator/fixed.c:289) platform_probe (drivers/base/platform.c:1427) [...] -> #1 (regulator_ww_class_acquire){+.+.}-{0:0}: regulator_lock_dependent (include/linux/ww_mutex.h:129 drivers/regulator/core.c:329) regulator_enable (drivers/regulator/core.c:2808) set_machine_constraints (drivers/regulator/core.c:1536) regulator_register (drivers/regulator/core.c:5486) devm_regulator_register (drivers/regulator/devres.c:196) reg_fixed_voltage_probe (drivers/regulator/fixed.c:289) [...] -> #0 (regulator_list_mutex){+.+.}-{3:3}: __lock_acquire (kernel/locking/lockdep.c:3052 (discriminator 4) kernel/locking/lockdep.c:3174 (discriminator 4) kernel/locking/lockdep.c:3789 (discriminator 4) kernel/locking/lockdep.c:5015 (discriminator 4)) lock_acquire (arch/arm64/include/asm/percpu.h:39 kernel/locking/lockdep.c:438 kernel/locking/lockdep.c:5627) __mutex_lock_common (include/asm-generic/atomic-instrumented.h:606 include/asm-generic/atomic-long.h:29 kernel/locking/mutex.c:103 kernel/locking/mutex.c:144 kernel/locking/mutex.c:963) mutex_lock_nested (kernel/locking/mutex.c:1125) regulator_lock_dependent (arch/arm64/include/asm/current.h:19 include/linux/ww_mutex.h:111 drivers/regulator/core.c:329) regulator_enable (drivers/regulator/core.c:2808) vctrl_enable (drivers/regulator/vctrl-regulator.c:400) _regulator_do_enable (drivers/regulator/core.c:2617) _regulator_enable (drivers/regulator/core.c:2764) regulator_enable (drivers/regulator/core.c:308 drivers/regulator/core.c:2809) _set_opp (drivers/opp/core.c:819 drivers/opp/core.c:1072) dev_pm_opp_set_rate (drivers/opp/core.c:1164) set_target (drivers/cpufreq/cpufreq-dt.c:62) __cpufreq_driver_target (drivers/cpufreq/cpufreq.c:2216 drivers/cpufreq/cpufreq.c:2271) cpufreq_online (drivers/cpufreq/cpufreq.c:1488 (discriminator 2)) cpufreq_add_dev (drivers/cpufreq/cpufreq.c:1563) subsys_interface_register (drivers/base/bus.c:?) cpufreq_register_driver (drivers/cpufreq/cpufreq.c:2819) dt_cpufreq_probe (drivers/cpufreq/cpufreq-dt.c:344) [...] other info that might help us debug this: Chain exists of: regulator_list_mutex --> regulator_ww_class_acquire --> regulator_ww_class_mutex Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(regulator_ww_class_mutex); lock(regulator_ww_class_acquire); lock(regulator_ww_class_mutex); lock(regulator_list_mutex); *** DEADLOCK *** 6 locks held by swapper/0/1: #0: ffffff8002d32188 (&dev->mutex){....}-{3:3}, at: __device_driver_lock (drivers/base/dd.c:1030) #1: ffffffc0111a0520 (cpu_hotplug_lock){++++}-{0:0}, at: cpufreq_register_driver (drivers/cpufreq/cpufreq.c:2792 (discriminator 2)) #2: ffffff8002a8d918 (subsys mutex#9){+.+.}-{3:3}, at: subsys_interface_register (drivers/base/bus.c:1033) #3: ffffff800341bb90 (&policy->rwsem){+.+.}-{3:3}, at: cpufreq_online (include/linux/bitmap.h:285 include/linux/cpumask.h:405 drivers/cpufreq/cpufreq.c:1399) #4: ffffffc011f0b7b8 (regulator_ww_class_acquire){+.+.}-{0:0}, at: regulator_enable (drivers/regulator/core.c:2808) #5: ffffff8004a77160 (regulator_ww_class_mutex){+.+.}-{3:3}, at: regulator_lock_recursive (drivers/regulator/core.c:156 drivers/regulator/core.c:263) stack backtrace: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.14.0-rc6 #2 7c8f8996d021ed0f65271e6aeebf7999de74a9fa Hardware name: Google Scarlet (DT) Call trace: dump_backtrace (arch/arm64/kernel/stacktrace.c:161) show_stack (arch/arm64/kernel/stacktrace.c:218) dump_stack_lvl (lib/dump_stack.c:106 (discriminator 2)) dump_stack (lib/dump_stack.c:113) print_circular_bug (kernel/locking/lockdep.c:?) check_noncircular (kernel/locking/lockdep.c:?) __lock_acquire (kernel/locking/lockdep.c:3052 (discriminator 4) kernel/locking/lockdep.c:3174 (discriminator 4) kernel/locking/lockdep.c:3789 (discriminator 4) kernel/locking/lockdep.c:5015 (discriminator 4)) lock_acquire (arch/arm64/include/asm/percpu.h:39 kernel/locking/lockdep.c:438 kernel/locking/lockdep.c:5627) __mutex_lock_common (include/asm-generic/atomic-instrumented.h:606 include/asm-generic/atomic-long.h:29 kernel/locking/mutex.c:103 kernel/locking/mutex.c:144 kernel/locking/mutex.c:963) mutex_lock_nested (kernel/locking/mutex.c:1125) regulator_lock_dependent (arch/arm64/include/asm/current.h:19 include/linux/ww_mutex.h:111 drivers/regulator/core.c:329) regulator_enable (drivers/regulator/core.c:2808) vctrl_enable (drivers/regulator/vctrl-regulator.c:400) _regulator_do_enable (drivers/regulator/core.c:2617) _regulator_enable (drivers/regulator/core.c:2764) regulator_enable (drivers/regulator/core.c:308 drivers/regulator/core.c:2809) _set_opp (drivers/opp/core.c:819 drivers/opp/core.c:1072) dev_pm_opp_set_rate (drivers/opp/core.c:1164) set_target (drivers/cpufreq/cpufreq-dt.c:62) __cpufreq_driver_target (drivers/cpufreq/cpufreq.c:2216 drivers/cpufreq/cpufreq.c:2271) cpufreq_online (drivers/cpufreq/cpufreq.c:1488 (discriminator 2)) cpufreq_add_dev (drivers/cpufreq/cpufreq.c:1563) subsys_interface_register (drivers/base/bus.c:?) cpufreq_register_driver (drivers/cpufreq/cpufreq.c:2819) dt_cpufreq_probe (drivers/cpufreq/cpufreq-dt.c:344) [...] Reported-by: Brian Norris Fixes: f8702f9e4aa7 ("regulator: core: Use ww_mutex for regulators locking") Fixes: e9153311491d ("regulator: vctrl-regulator: Avoid deadlock getting and setting the voltage") Signed-off-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20210825033704.3307263-3-wenst@chromium.org Signed-off-by: Mark Brown --- drivers/regulator/vctrl-regulator.c | 72 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c index 93d33201ffe0..d2a37978fc3a 100644 --- a/drivers/regulator/vctrl-regulator.c +++ b/drivers/regulator/vctrl-regulator.c @@ -37,7 +37,6 @@ struct vctrl_voltage_table { struct vctrl_data { struct regulator_dev *rdev; struct regulator_desc desc; - struct regulator *ctrl_reg; bool enabled; unsigned int min_slew_down_rate; unsigned int ovp_threshold; @@ -82,7 +81,12 @@ static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV) static int vctrl_get_voltage(struct regulator_dev *rdev) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); - int ctrl_uV = regulator_get_voltage_rdev(vctrl->ctrl_reg->rdev); + int ctrl_uV; + + if (!rdev->supply) + return -EPROBE_DEFER; + + ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); return vctrl_calc_output_voltage(vctrl, ctrl_uV); } @@ -92,14 +96,19 @@ static int vctrl_set_voltage(struct regulator_dev *rdev, unsigned int *selector) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); - struct regulator *ctrl_reg = vctrl->ctrl_reg; - int orig_ctrl_uV = regulator_get_voltage_rdev(ctrl_reg->rdev); - int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); + int orig_ctrl_uV; + int uV; int ret; + if (!rdev->supply) + return -EPROBE_DEFER; + + orig_ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); + uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); + if (req_min_uV >= uV || !vctrl->ovp_threshold) /* voltage rising or no OVP */ - return regulator_set_voltage_rdev(ctrl_reg->rdev, + return regulator_set_voltage_rdev(rdev->supply->rdev, vctrl_calc_ctrl_voltage(vctrl, req_min_uV), vctrl_calc_ctrl_voltage(vctrl, req_max_uV), PM_SUSPEND_ON); @@ -117,7 +126,7 @@ static int vctrl_set_voltage(struct regulator_dev *rdev, next_uV = max_t(int, req_min_uV, uV - max_drop_uV); next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV); - ret = regulator_set_voltage_rdev(ctrl_reg->rdev, + ret = regulator_set_voltage_rdev(rdev->supply->rdev, next_ctrl_uV, next_ctrl_uV, PM_SUSPEND_ON); @@ -134,7 +143,7 @@ static int vctrl_set_voltage(struct regulator_dev *rdev, err: /* Try to go back to original voltage */ - regulator_set_voltage_rdev(ctrl_reg->rdev, orig_ctrl_uV, orig_ctrl_uV, + regulator_set_voltage_rdev(rdev->supply->rdev, orig_ctrl_uV, orig_ctrl_uV, PM_SUSPEND_ON); return ret; @@ -151,16 +160,18 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); - struct regulator *ctrl_reg = vctrl->ctrl_reg; unsigned int orig_sel = vctrl->sel; int ret; + if (!rdev->supply) + return -EPROBE_DEFER; + if (selector >= rdev->desc->n_voltages) return -EINVAL; if (selector >= vctrl->sel || !vctrl->ovp_threshold) { /* voltage rising or no OVP */ - ret = regulator_set_voltage_rdev(ctrl_reg->rdev, + ret = regulator_set_voltage_rdev(rdev->supply->rdev, vctrl->vtable[selector].ctrl, vctrl->vtable[selector].ctrl, PM_SUSPEND_ON); @@ -179,7 +190,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev, else next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel; - ret = regulator_set_voltage_rdev(ctrl_reg->rdev, + ret = regulator_set_voltage_rdev(rdev->supply->rdev, vctrl->vtable[next_sel].ctrl, vctrl->vtable[next_sel].ctrl, PM_SUSPEND_ON); @@ -202,7 +213,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev, err: if (vctrl->sel != orig_sel) { /* Try to go back to original voltage */ - if (!regulator_set_voltage_rdev(ctrl_reg->rdev, + if (!regulator_set_voltage_rdev(rdev->supply->rdev, vctrl->vtable[orig_sel].ctrl, vctrl->vtable[orig_sel].ctrl, PM_SUSPEND_ON)) @@ -234,10 +245,6 @@ static int vctrl_parse_dt(struct platform_device *pdev, u32 pval; u32 vrange_ctrl[2]; - vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl"); - if (IS_ERR(vctrl->ctrl_reg)) - return PTR_ERR(vctrl->ctrl_reg); - ret = of_property_read_u32(np, "ovp-threshold-percent", &pval); if (!ret) { vctrl->ovp_threshold = pval; @@ -315,11 +322,11 @@ static int vctrl_cmp_ctrl_uV(const void *a, const void *b) return at->ctrl - bt->ctrl; } -static int vctrl_init_vtable(struct platform_device *pdev) +static int vctrl_init_vtable(struct platform_device *pdev, + struct regulator *ctrl_reg) { struct vctrl_data *vctrl = platform_get_drvdata(pdev); struct regulator_desc *rdesc = &vctrl->desc; - struct regulator *ctrl_reg = vctrl->ctrl_reg; struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl; int n_voltages; int ctrl_uV; @@ -395,23 +402,19 @@ static int vctrl_init_vtable(struct platform_device *pdev) static int vctrl_enable(struct regulator_dev *rdev) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); - int ret = regulator_enable(vctrl->ctrl_reg); - if (!ret) - vctrl->enabled = true; + vctrl->enabled = true; - return ret; + return 0; } static int vctrl_disable(struct regulator_dev *rdev) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); - int ret = regulator_disable(vctrl->ctrl_reg); - if (!ret) - vctrl->enabled = false; + vctrl->enabled = false; - return ret; + return 0; } static int vctrl_is_enabled(struct regulator_dev *rdev) @@ -447,6 +450,7 @@ static int vctrl_probe(struct platform_device *pdev) struct regulator_desc *rdesc; struct regulator_config cfg = { }; struct vctrl_voltage_range *vrange_ctrl; + struct regulator *ctrl_reg; int ctrl_uV; int ret; @@ -461,15 +465,20 @@ static int vctrl_probe(struct platform_device *pdev) if (ret) return ret; + ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl"); + if (IS_ERR(ctrl_reg)) + return PTR_ERR(ctrl_reg); + vrange_ctrl = &vctrl->vrange.ctrl; rdesc = &vctrl->desc; rdesc->name = "vctrl"; rdesc->type = REGULATOR_VOLTAGE; rdesc->owner = THIS_MODULE; + rdesc->supply_name = "ctrl"; - if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) || - (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) { + if ((regulator_get_linear_step(ctrl_reg) == 1) || + (regulator_count_voltages(ctrl_reg) == -EINVAL)) { rdesc->continuous_voltage_range = true; rdesc->ops = &vctrl_ops_cont; } else { @@ -486,12 +495,12 @@ static int vctrl_probe(struct platform_device *pdev) cfg.init_data = init_data; if (!rdesc->continuous_voltage_range) { - ret = vctrl_init_vtable(pdev); + ret = vctrl_init_vtable(pdev, ctrl_reg); if (ret) return ret; /* Use locked consumer API when not in regulator framework */ - ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg); + ctrl_uV = regulator_get_voltage(ctrl_reg); if (ctrl_uV < 0) { dev_err(&pdev->dev, "failed to get control voltage\n"); return ctrl_uV; @@ -514,6 +523,9 @@ static int vctrl_probe(struct platform_device *pdev) } } + /* Drop ctrl-supply here in favor of regulator core managed supply */ + devm_regulator_put(ctrl_reg); + vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg); if (IS_ERR(vctrl->rdev)) { ret = PTR_ERR(vctrl->rdev); From 67021f25d95292d285dd213c58401642b98eaf24 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 25 Aug 2021 23:50:40 +0300 Subject: [PATCH 169/177] regmap: teach regmap to use raw spinlocks if requested in the config Some drivers might access regmap in a context where a raw spinlock is held. An example is drivers/irqchip/irq-ls-extirq.c, which calls regmap_update_bits() from struct irq_chip :: irq_set_type, which is a method called by __irq_set_trigger() under the desc->lock raw spin lock. Since desc->lock is a raw spin lock and the regmap internal lock for mmio is a plain spinlock (which can become sleepable on RT), this is an invalid locking scheme and we get a splat stating that this is a "[ BUG: Invalid wait context ]". It seems reasonable for regmap to have an option use a raw spinlock too, so add that in the config such that drivers can request it. Suggested-by: Mark Brown Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20210825205041.927788-2-vladimir.oltean@nxp.com Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap.c | 35 +++++++++++++++++++++++++++++----- include/linux/regmap.h | 2 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 0097696c31de..b1905916f7af 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -53,6 +53,10 @@ struct regmap { spinlock_t spinlock; unsigned long spinlock_flags; }; + struct { + raw_spinlock_t raw_spinlock; + unsigned long raw_spinlock_flags; + }; }; regmap_lock lock; regmap_unlock unlock; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 297e95be25b3..d8510708ec54 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -523,6 +523,23 @@ __releases(&map->spinlock) spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } +static void regmap_lock_raw_spinlock(void *__map) +__acquires(&map->raw_spinlock) +{ + struct regmap *map = __map; + unsigned long flags; + + raw_spin_lock_irqsave(&map->raw_spinlock, flags); + map->raw_spinlock_flags = flags; +} + +static void regmap_unlock_raw_spinlock(void *__map) +__releases(&map->raw_spinlock) +{ + struct regmap *map = __map; + raw_spin_unlock_irqrestore(&map->raw_spinlock, map->raw_spinlock_flags); +} + static void dev_get_regmap_release(struct device *dev, void *res) { /* @@ -760,11 +777,19 @@ struct regmap *__regmap_init(struct device *dev, } else { if ((bus && bus->fast_io) || config->fast_io) { - spin_lock_init(&map->spinlock); - map->lock = regmap_lock_spinlock; - map->unlock = regmap_unlock_spinlock; - lockdep_set_class_and_name(&map->spinlock, - lock_key, lock_name); + if (config->use_raw_spinlock) { + raw_spin_lock_init(&map->raw_spinlock); + map->lock = regmap_lock_raw_spinlock; + map->unlock = regmap_unlock_raw_spinlock; + lockdep_set_class_and_name(&map->raw_spinlock, + lock_key, lock_name); + } else { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + lockdep_set_class_and_name(&map->spinlock, + lock_key, lock_name); + } } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f87a11a5cc4a..ed57b7c5b468 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -343,6 +343,7 @@ typedef void (*regmap_unlock)(void *); * @ranges: Array of configuration entries for virtual address ranges. * @num_ranges: Number of range configuration entries. * @use_hwlock: Indicate if a hardware spinlock should be used. + * @use_raw_spinlock: Indicate if a raw spinlock should be used. * @hwlock_id: Specify the hardware spinlock id. * @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE, * HWLOCK_IRQ or 0. @@ -402,6 +403,7 @@ struct regmap_config { unsigned int num_ranges; bool use_hwlock; + bool use_raw_spinlock; unsigned int hwlock_id; unsigned int hwlock_mode; From 245ca2cc212bb2a078332ec99afbfbb202f44c2d Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 26 Aug 2021 17:15:46 +0800 Subject: [PATCH 170/177] spi: sprd: Fix the wrong WDG_LOAD_VAL Use 50ms as default timeout value and the time clock is 32768HZ. The original value of WDG_LOAD_VAL is not correct, so this patch fixes it. Fixes: ac1775012058 ("spi: sprd: Add the support of restarting the system") Signed-off-by: Chunyan Zhang Link: https://lore.kernel.org/r/20210826091549.2138125-2-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 07f11b17bf20..d392dc6db927 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -103,7 +103,7 @@ #define HWRST_STATUS_WATCHDOG 0xf0 /* Use default timeout 50 ms that converts to watchdog values */ -#define WDG_LOAD_VAL ((50 * 1000) / 32768) +#define WDG_LOAD_VAL ((50 * 32768) / 1000) #define WDG_LOAD_MASK GENMASK(15, 0) #define WDG_UNLOCK_KEY 0xe551 From 3b66ca9783d1d1b7be7bf41e8934ca2eaf50a9c0 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 26 Aug 2021 17:15:47 +0800 Subject: [PATCH 171/177] spi: sprd: Add ADI r3 support ADI r3p0 is used on SC9863 and UMS512 SoCs. Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20210826091549.2138125-3-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 215 ++++++++++++++++++++++++++++--------- 1 file changed, 165 insertions(+), 50 deletions(-) diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index d392dc6db927..1edbf44c05a7 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -52,10 +52,20 @@ /* * ADI slave devices include RTC, ADC, regulator, charger, thermal and so on. - * The slave devices address offset is always 0x8000 and size is 4K. + * ADI supports 12/14bit address for r2p0, and additional 17bit for r3p0 or + * later versions. Since bit[1:0] are zero, so the spec describe them as + * 10/12/15bit address mode. + * The 10bit mode supports sigle slave, 12/15bit mode supports 3 slave, the + * high two bits is slave_id. + * The slave devices address offset is 0x8000 for 10/12bit address mode, + * and 0x20000 for 15bit mode. */ -#define ADI_SLAVE_ADDR_SIZE SZ_4K -#define ADI_SLAVE_OFFSET 0x8000 +#define ADI_10BIT_SLAVE_ADDR_SIZE SZ_4K +#define ADI_10BIT_SLAVE_OFFSET 0x8000 +#define ADI_12BIT_SLAVE_ADDR_SIZE SZ_16K +#define ADI_12BIT_SLAVE_OFFSET 0x8000 +#define ADI_15BIT_SLAVE_ADDR_SIZE SZ_128K +#define ADI_15BIT_SLAVE_OFFSET 0x20000 /* Timeout (ms) for the trylock of hardware spinlocks */ #define ADI_HWSPINLOCK_TIMEOUT 5000 @@ -67,24 +77,35 @@ #define ADI_FIFO_DRAIN_TIMEOUT 1000 #define ADI_READ_TIMEOUT 2000 -#define REG_ADDR_LOW_MASK GENMASK(11, 0) + +/* + * Read back address from REG_ADI_RD_DATA bit[30:16] which maps to: + * REG_ADI_RD_CMD bit[14:0] for r2p0 + * REG_ADI_RD_CMD bit[16:2] for r3p0 + */ +#define RDBACK_ADDR_MASK_R2 GENMASK(14, 0) +#define RDBACK_ADDR_MASK_R3 GENMASK(16, 2) +#define RDBACK_ADDR_SHIFT_R3 2 /* Registers definitions for PMIC watchdog controller */ -#define REG_WDG_LOAD_LOW 0x80 -#define REG_WDG_LOAD_HIGH 0x84 -#define REG_WDG_CTRL 0x88 -#define REG_WDG_LOCK 0xa0 +#define REG_WDG_LOAD_LOW 0x0 +#define REG_WDG_LOAD_HIGH 0x4 +#define REG_WDG_CTRL 0x8 +#define REG_WDG_LOCK 0x20 /* Bits definitions for register REG_WDG_CTRL */ #define BIT_WDG_RUN BIT(1) #define BIT_WDG_NEW BIT(2) #define BIT_WDG_RST BIT(3) +/* Bits definitions for register REG_MODULE_EN */ +#define BIT_WDG_EN BIT(2) + /* Registers definitions for PMIC */ #define PMIC_RST_STATUS 0xee8 #define PMIC_MODULE_EN 0xc08 #define PMIC_CLK_EN 0xc18 -#define BIT_WDG_EN BIT(2) +#define PMIC_WDG_BASE 0x80 /* Definition of PMIC reset status register */ #define HWRST_STATUS_SECURITY 0x02 @@ -107,6 +128,22 @@ #define WDG_LOAD_MASK GENMASK(15, 0) #define WDG_UNLOCK_KEY 0xe551 +struct sprd_adi_wdg { + u32 base; + u32 rst_sts; + u32 wdg_en; + u32 wdg_clk; +}; + +struct sprd_adi_data { + u32 slave_offset; + u32 slave_addr_size; + int (*read_check)(u32 val, u32 reg); + int (*restart)(struct notifier_block *this, + unsigned long mode, void *cmd); + void (*wdg_rst)(void *p); +}; + struct sprd_adi { struct spi_controller *ctlr; struct device *dev; @@ -115,11 +152,12 @@ struct sprd_adi { unsigned long slave_vbase; unsigned long slave_pbase; struct notifier_block restart_handler; + const struct sprd_adi_data *data; }; static int sprd_adi_check_addr(struct sprd_adi *sadi, u32 reg) { - if (reg >= ADI_SLAVE_ADDR_SIZE) { + if (reg >= sadi->data->slave_addr_size) { dev_err(sadi->dev, "slave address offset is incorrect, reg = 0x%x\n", reg); @@ -155,11 +193,35 @@ static int sprd_adi_fifo_is_full(struct sprd_adi *sadi) return readl_relaxed(sadi->base + REG_ADI_ARM_FIFO_STS) & BIT_FIFO_FULL; } +static int sprd_adi_read_check(u32 val, u32 addr) +{ + u32 rd_addr; + + rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT; + + if (rd_addr != addr) { + pr_err("ADI read error, addr = 0x%x, val = 0x%x\n", addr, val); + return -EIO; + } + + return 0; +} + +static int sprd_adi_read_check_r2(u32 val, u32 reg) +{ + return sprd_adi_read_check(val, reg & RDBACK_ADDR_MASK_R2); +} + +static int sprd_adi_read_check_r3(u32 val, u32 reg) +{ + return sprd_adi_read_check(val, (reg & RDBACK_ADDR_MASK_R3) >> RDBACK_ADDR_SHIFT_R3); +} + static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) { int read_timeout = ADI_READ_TIMEOUT; unsigned long flags; - u32 val, rd_addr; + u32 val; int ret = 0; if (sadi->hwlock) { @@ -203,18 +265,15 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val) } /* - * The return value includes data and read register address, from bit 0 - * to bit 15 are data, and from bit 16 to bit 30 are read register - * address. Then we can check the returned register address to validate - * data. + * The return value before adi r5p0 includes data and read register + * address, from bit 0to bit 15 are data, and from bit 16 to bit 30 + * are read register address. Then we can check the returned register + * address to validate data. */ - rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT; - - if (rd_addr != (reg & REG_ADDR_LOW_MASK)) { - dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n", - reg, val); - ret = -EIO; - goto out; + if (sadi->data->read_check) { + ret = sadi->data->read_check(val, reg); + if (ret < 0) + goto out; } *read_val = val & RD_VALUE_MASK; @@ -299,20 +358,21 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, return ret; } -static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) +static void sprd_adi_set_wdt_rst_mode(void *p) { #if IS_ENABLED(CONFIG_SPRD_WATCHDOG) u32 val; + struct sprd_adi *sadi = (struct sprd_adi *)p; - /* Set default watchdog reboot mode */ + /* Init watchdog reset mode */ sprd_adi_read(sadi, PMIC_RST_STATUS, &val); val |= HWRST_STATUS_WATCHDOG; sprd_adi_write(sadi, PMIC_RST_STATUS, val); #endif } -static int sprd_adi_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int sprd_adi_restart(struct notifier_block *this, unsigned long mode, + void *cmd, struct sprd_adi_wdg *wdg) { struct sprd_adi *sadi = container_of(this, struct sprd_adi, restart_handler); @@ -348,40 +408,40 @@ static int sprd_adi_restart_handler(struct notifier_block *this, reboot_mode = HWRST_STATUS_NORMAL; /* Record the reboot mode */ - sprd_adi_read(sadi, PMIC_RST_STATUS, &val); + sprd_adi_read(sadi, wdg->rst_sts, &val); val &= ~HWRST_STATUS_WATCHDOG; val |= reboot_mode; - sprd_adi_write(sadi, PMIC_RST_STATUS, val); + sprd_adi_write(sadi, wdg->rst_sts, val); /* Enable the interface clock of the watchdog */ - sprd_adi_read(sadi, PMIC_MODULE_EN, &val); + sprd_adi_read(sadi, wdg->wdg_en, &val); val |= BIT_WDG_EN; - sprd_adi_write(sadi, PMIC_MODULE_EN, val); + sprd_adi_write(sadi, wdg->wdg_en, val); /* Enable the work clock of the watchdog */ - sprd_adi_read(sadi, PMIC_CLK_EN, &val); + sprd_adi_read(sadi, wdg->wdg_clk, &val); val |= BIT_WDG_EN; - sprd_adi_write(sadi, PMIC_CLK_EN, val); + sprd_adi_write(sadi, wdg->wdg_clk, val); /* Unlock the watchdog */ - sprd_adi_write(sadi, REG_WDG_LOCK, WDG_UNLOCK_KEY); + sprd_adi_write(sadi, wdg->base + REG_WDG_LOCK, WDG_UNLOCK_KEY); - sprd_adi_read(sadi, REG_WDG_CTRL, &val); + sprd_adi_read(sadi, wdg->base + REG_WDG_CTRL, &val); val |= BIT_WDG_NEW; - sprd_adi_write(sadi, REG_WDG_CTRL, val); + sprd_adi_write(sadi, wdg->base + REG_WDG_CTRL, val); /* Load the watchdog timeout value, 50ms is always enough. */ - sprd_adi_write(sadi, REG_WDG_LOAD_HIGH, 0); - sprd_adi_write(sadi, REG_WDG_LOAD_LOW, + sprd_adi_write(sadi, wdg->base + REG_WDG_LOAD_HIGH, 0); + sprd_adi_write(sadi, wdg->base + REG_WDG_LOAD_LOW, WDG_LOAD_VAL & WDG_LOAD_MASK); /* Start the watchdog to reset system */ - sprd_adi_read(sadi, REG_WDG_CTRL, &val); + sprd_adi_read(sadi, wdg->base + REG_WDG_CTRL, &val); val |= BIT_WDG_RUN | BIT_WDG_RST; - sprd_adi_write(sadi, REG_WDG_CTRL, val); + sprd_adi_write(sadi, wdg->base + REG_WDG_CTRL, val); /* Lock the watchdog */ - sprd_adi_write(sadi, REG_WDG_LOCK, ~WDG_UNLOCK_KEY); + sprd_adi_write(sadi, wdg->base + REG_WDG_LOCK, ~WDG_UNLOCK_KEY); mdelay(1000); @@ -389,6 +449,19 @@ static int sprd_adi_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } +static int sprd_adi_restart_sc9860(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct sprd_adi_wdg wdg = { + .base = PMIC_WDG_BASE, + .rst_sts = PMIC_RST_STATUS, + .wdg_en = PMIC_MODULE_EN, + .wdg_clk = PMIC_CLK_EN, + }; + + return sprd_adi_restart(this, mode, cmd, &wdg); +} + static void sprd_adi_hw_init(struct sprd_adi *sadi) { struct device_node *np = sadi->dev->of_node; @@ -440,10 +513,11 @@ static void sprd_adi_hw_init(struct sprd_adi *sadi) static int sprd_adi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const struct sprd_adi_data *data; struct spi_controller *ctlr; struct sprd_adi *sadi; struct resource *res; - u32 num_chipselect; + u16 num_chipselect; int ret; if (!np) { @@ -451,6 +525,12 @@ static int sprd_adi_probe(struct platform_device *pdev) return -ENODEV; } + data = of_device_get_match_data(&pdev->dev); + if (!data) { + dev_err(&pdev->dev, "no matching driver data found\n"); + return -EINVAL; + } + pdev->id = of_alias_get_id(np, "spi"); num_chipselect = of_get_child_count(np); @@ -468,10 +548,12 @@ static int sprd_adi_probe(struct platform_device *pdev) goto put_ctlr; } - sadi->slave_vbase = (unsigned long)sadi->base + ADI_SLAVE_OFFSET; - sadi->slave_pbase = res->start + ADI_SLAVE_OFFSET; + sadi->slave_vbase = (unsigned long)sadi->base + + data->slave_offset; + sadi->slave_pbase = res->start + data->slave_offset; sadi->ctlr = ctlr; sadi->dev = &pdev->dev; + sadi->data = data; ret = of_hwspin_lock_get_id(np, 0); if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) { sadi->hwlock = @@ -492,7 +574,9 @@ static int sprd_adi_probe(struct platform_device *pdev) } sprd_adi_hw_init(sadi); - sprd_adi_set_wdt_rst_mode(sadi); + + if (sadi->data->wdg_rst) + sadi->data->wdg_rst(sadi); ctlr->dev.of_node = pdev->dev.of_node; ctlr->bus_num = pdev->id; @@ -507,12 +591,14 @@ static int sprd_adi_probe(struct platform_device *pdev) goto put_ctlr; } - sadi->restart_handler.notifier_call = sprd_adi_restart_handler; - sadi->restart_handler.priority = 128; - ret = register_restart_handler(&sadi->restart_handler); - if (ret) { - dev_err(&pdev->dev, "can not register restart handler\n"); - goto put_ctlr; + if (sadi->data->restart) { + sadi->restart_handler.notifier_call = sadi->data->restart; + sadi->restart_handler.priority = 128; + ret = register_restart_handler(&sadi->restart_handler); + if (ret) { + dev_err(&pdev->dev, "can not register restart handler\n"); + goto put_ctlr; + } } return 0; @@ -531,9 +617,38 @@ static int sprd_adi_remove(struct platform_device *pdev) return 0; } +static struct sprd_adi_data sc9860_data = { + .slave_offset = ADI_10BIT_SLAVE_OFFSET, + .slave_addr_size = ADI_10BIT_SLAVE_ADDR_SIZE, + .read_check = sprd_adi_read_check_r2, + .restart = sprd_adi_restart_sc9860, + .wdg_rst = sprd_adi_set_wdt_rst_mode, +}; + +static struct sprd_adi_data sc9863_data = { + .slave_offset = ADI_12BIT_SLAVE_OFFSET, + .slave_addr_size = ADI_12BIT_SLAVE_ADDR_SIZE, + .read_check = sprd_adi_read_check_r3, +}; + +static struct sprd_adi_data ums512_data = { + .slave_offset = ADI_15BIT_SLAVE_OFFSET, + .slave_addr_size = ADI_15BIT_SLAVE_ADDR_SIZE, + .read_check = sprd_adi_read_check_r3, +}; + static const struct of_device_id sprd_adi_of_match[] = { { .compatible = "sprd,sc9860-adi", + .data = &sc9860_data, + }, + { + .compatible = "sprd,sc9863-adi", + .data = &sc9863_data, + }, + { + .compatible = "sprd,ums512-adi", + .data = &ums512_data, }, { }, }; From f15e60d460391d16bdad2e446e9dca4f264ccdfe Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 26 Aug 2021 17:15:48 +0800 Subject: [PATCH 172/177] spi: Convert sprd ADI bindings to yaml Convert spi-sprd-adi.txt to yaml. Signed-off-by: Chunyan Zhang Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210826091549.2138125-4-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sprd-adi.txt | 63 ----------- .../devicetree/bindings/spi/sprd,spi-adi.yaml | 102 ++++++++++++++++++ 2 files changed, 102 insertions(+), 63 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/spi-sprd-adi.txt create mode 100644 Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml diff --git a/Documentation/devicetree/bindings/spi/spi-sprd-adi.txt b/Documentation/devicetree/bindings/spi/spi-sprd-adi.txt deleted file mode 100644 index 2567c829e2dc..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-sprd-adi.txt +++ /dev/null @@ -1,63 +0,0 @@ -Spreadtrum ADI controller - -ADI is the abbreviation of Anolog-Digital interface, which is used to access -analog chip (such as PMIC) from digital chip. ADI controller follows the SPI -framework for its hardware implementation is alike to SPI bus and its timing -is compatile to SPI timing. - -ADI controller has 50 channels including 2 software read/write channels and -48 hardware channels to access analog chip. For 2 software read/write channels, -users should set ADI registers to access analog chip. For hardware channels, -we can configure them to allow other hardware components to use it independently, -which means we can just link one analog chip address to one hardware channel, -then users can access the mapped analog chip address by this hardware channel -triggered by hardware components instead of ADI software channels. - -Thus we introduce one property named "sprd,hw-channels" to configure hardware -channels, the first value specifies the hardware channel id which is used to -transfer data triggered by hardware automatically, and the second value specifies -the analog chip address where user want to access by hardware components. - -Since we have multi-subsystems will use unique ADI to access analog chip, when -one system is reading/writing data by ADI software channels, that should be under -one hardware spinlock protection to prevent other systems from reading/writing -data by ADI software channels at the same time, or two parallel routine of setting -ADI registers will make ADI controller registers chaos to lead incorrect results. -Then we need one hardware spinlock to synchronize between the multiple subsystems. - -The new version ADI controller supplies multiple master channels for different -subsystem accessing, that means no need to add hardware spinlock to synchronize, -thus change the hardware spinlock support to be optional to keep backward -compatibility. - -Required properties: -- compatible: Should be "sprd,sc9860-adi". -- reg: Offset and length of ADI-SPI controller register space. -- #address-cells: Number of cells required to define a chip select address - on the ADI-SPI bus. Should be set to 1. -- #size-cells: Size of cells required to define a chip select address size - on the ADI-SPI bus. Should be set to 0. - -Optional properties: -- hwlocks: Reference to a phandle of a hwlock provider node. -- hwlock-names: Reference to hwlock name strings defined in the same order - as the hwlocks, should be "adi". -- sprd,hw-channels: This is an array of channel values up to 49 channels. - The first value specifies the hardware channel id which is used to - transfer data triggered by hardware automatically, and the second - value specifies the analog chip address where user want to access - by hardware components. - -SPI slave nodes must be children of the SPI controller node and can contain -properties described in Documentation/devicetree/bindings/spi/spi-bus.txt. - -Example: - adi_bus: spi@40030000 { - compatible = "sprd,sc9860-adi"; - reg = <0 0x40030000 0 0x10000>; - hwlocks = <&hwlock1 0>; - hwlock-names = "adi"; - #address-cells = <1>; - #size-cells = <0>; - sprd,hw-channels = <30 0x8c20>; - }; diff --git a/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml b/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml new file mode 100644 index 000000000000..3e399d31168b --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/spi/sprd,spi-adi.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Spreadtrum ADI controller + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +description: | + ADI is the abbreviation of Anolog-Digital interface, which is used to access + analog chip (such as PMIC) from digital chip. ADI controller follows the SPI + framework for its hardware implementation is alike to SPI bus and its timing + is compatile to SPI timing. + + ADI controller has 50 channels including 2 software read/write channels and + 48 hardware channels to access analog chip. For 2 software read/write channels, + users should set ADI registers to access analog chip. For hardware channels, + we can configure them to allow other hardware components to use it independently, + which means we can just link one analog chip address to one hardware channel, + then users can access the mapped analog chip address by this hardware channel + triggered by hardware components instead of ADI software channels. + + Thus we introduce one property named "sprd,hw-channels" to configure hardware + channels, the first value specifies the hardware channel id which is used to + transfer data triggered by hardware automatically, and the second value specifies + the analog chip address where user want to access by hardware components. + + Since we have multi-subsystems will use unique ADI to access analog chip, when + one system is reading/writing data by ADI software channels, that should be under + one hardware spinlock protection to prevent other systems from reading/writing + data by ADI software channels at the same time, or two parallel routine of setting + ADI registers will make ADI controller registers chaos to lead incorrect results. + Then we need one hardware spinlock to synchronize between the multiple subsystems. + + The new version ADI controller supplies multiple master channels for different + subsystem accessing, that means no need to add hardware spinlock to synchronize, + thus change the hardware spinlock support to be optional to keep backward + compatibility. + +allOf: + - $ref: /spi/spi-controller.yaml# + +properties: + compatible: + enum: + - sprd,sc9860-adi + + reg: + maxItems: 1 + + hwlocks: + maxItems: 1 + + hwlock-names: + const: adi + + sprd,hw-channels: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + description: A list of hardware channels + minItems: 1 + maxItems: 48 + items: + items: + - description: The hardware channel id which is used to transfer data + triggered by hardware automatically, channel id 0-1 are for software + use, 2-49 are hardware channels. + minimum: 2 + maximum: 49 + - description: The analog chip address where user want to access by + hardware components. + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + aon { + #address-cells = <2>; + #size-cells = <2>; + + adi_bus: spi@40030000 { + compatible = "sprd,sc9860-adi"; + reg = <0 0x40030000 0 0x10000>; + hwlocks = <&hwlock1 0>; + hwlock-names = "adi"; + #address-cells = <1>; + #size-cells = <0>; + sprd,hw-channels = <30 0x8c20>; + }; + }; +... From 0f887ac82971cbde59e563d6490c05c6b15aa82f Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 26 Aug 2021 17:15:49 +0800 Subject: [PATCH 173/177] spi: add sprd ADI for sc9863 and ums512 This patch adds support for sc9863 and ums512. Signed-off-by: Chunyan Zhang Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210826091549.2138125-5-zhang.lyra@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml b/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml index 3e399d31168b..fe014020da69 100644 --- a/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml +++ b/Documentation/devicetree/bindings/spi/sprd,spi-adi.yaml @@ -50,6 +50,8 @@ properties: compatible: enum: - sprd,sc9860-adi + - sprd,sc9863-adi + - sprd,ums512-adi reg: maxItems: 1 From 26cfc0dbe43aae60dc03af27077775244f26c167 Mon Sep 17 00:00:00 2001 From: Quanyang Wang Date: Thu, 26 Aug 2021 08:59:30 +0800 Subject: [PATCH 174/177] spi: spi-zynq-qspi: use wait_for_completion_timeout to make zynq_qspi_exec_mem_op not interruptible The function wait_for_completion_interruptible_timeout will return -ERESTARTSYS immediately when receiving SIGKILL signal which is sent by "jffs2_gcd_mtd" during umounting jffs2. This will break the SPI memory operation because the data transmitting may begin before the command or address transmitting completes. Use wait_for_completion_timeout to prevent the process from being interruptible. Fixes: 67dca5e580f1 ("spi: spi-mem: Add support for Zynq QSPI controller") Signed-off-by: Quanyang Wang Link: https://lore.kernel.org/r/20210826005930.20572-1-quanyang.wang@windriver.com Signed-off-by: Mark Brown --- drivers/spi/spi-zynq-qspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index 9262c6418463..cfa222c9bd5e 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -545,7 +545,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem, zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_RXTX_MASK); - if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, + if (!wait_for_completion_timeout(&xqspi->data_completion, msecs_to_jiffies(1000))) err = -ETIMEDOUT; } @@ -563,7 +563,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem, zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_RXTX_MASK); - if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, + if (!wait_for_completion_timeout(&xqspi->data_completion, msecs_to_jiffies(1000))) err = -ETIMEDOUT; } @@ -579,7 +579,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem, zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_RXTX_MASK); - if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, + if (!wait_for_completion_timeout(&xqspi->data_completion, msecs_to_jiffies(1000))) err = -ETIMEDOUT; @@ -603,7 +603,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem, zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_RXTX_MASK); - if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion, + if (!wait_for_completion_timeout(&xqspi->data_completion, msecs_to_jiffies(1000))) err = -ETIMEDOUT; } From 291d47ccad191322524d77e0769dadcc8a811630 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 21 Jul 2021 12:01:28 -0700 Subject: [PATCH 175/177] string: improve default out-of-line memcmp() implementation This just does the "if the architecture does efficient unaligned handling, start the memcmp using 'unsigned long' accesses", since Nikolay Borisov found a load that cares. This is basically the minimal patch, and limited to architectures that are known to not have slow unaligned handling. We've had the stupid byte-at-a-time version forever, and nobody has ever even noticed before, so let's keep the fix minimal. A potential further improvement would be to align one of the sources in order to at least minimize unaligned cases, but the only real case of bigger memcmp() users seems to be the FIDEDUPERANGE ioctl(). As David Sterba says, the dedupe ioctl is typically called on ranges spanning many pages so the common case will all be page-aligned anyway. All the relevant architectures select HAVE_EFFICIENT_UNALIGNED_ACCESS, so I'm not going to worry about the combination of a very rare use-case and a rare architecture until somebody actually hits it. Particularly since Nikolay also tested the more complex patch with extra alignment handling code, and it only added overhead. Link: https://lore.kernel.org/lkml/20210721135926.602840-1-nborisov@suse.com/ Reported-by: Nikolay Borisov Cc: David Sterba Signed-off-by: Linus Torvalds --- lib/string.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/string.c b/lib/string.c index 77bd0b1d3296..b2de45a581f4 100644 --- a/lib/string.c +++ b/lib/string.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -935,6 +936,21 @@ __visible int memcmp(const void *cs, const void *ct, size_t count) const unsigned char *su1, *su2; int res = 0; +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (count >= sizeof(unsigned long)) { + const unsigned long *u1 = cs; + const unsigned long *u2 = ct; + do { + if (get_unaligned(u1) != get_unaligned(u2)) + break; + u1++; + u2++; + count -= sizeof(unsigned long); + } while (count >= sizeof(unsigned long)); + cs = u1; + ct = u2; + } +#endif for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) if ((res = *su1 - *su2) != 0) break; From 3a2b2eb55681158d3e3ef464fbf47574cf0c517c Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 30 Aug 2021 04:56:15 -0400 Subject: [PATCH 176/177] console: consume APC, DM, DCS The Linux console's VT102 implementation already consumes OSC ("Operating System Command") sequences, probably because that's how palette changes are transmitted. In addition to OSC, there are three other major clases of ANSI control strings: APC ("Application Program Command"), PM ("Privacy Message"), and DCS ("Device Control String"). They are handled similarly to OSC in terms of termination. Source: vt100.net Add three new enumerated states, one for each of these types. All three are handled the same way right now--they simply consume input until terminated. I hope to expand upon this firmament in the future. Add new predicate ansi_control_string(), returning true for any of these states. Replace explicit checks against ESosc with calls to this function. Transition to these states appropriately from the escape initiation (ESesc) state. This was motivated by the following Notcurses bugs: https://github.com/dankamongmen/notcurses/issues/2050 https://github.com/dankamongmen/notcurses/issues/1828 https://github.com/dankamongmen/notcurses/issues/2069 where standard VT sequences are not consumed by the Linux console. It's not necessary that the Linux console *support* these sequences, but it ought *consume* these well-specified classes of sequences. Tested by sending a variety of escape sequences to the console, and verifying that they still worked, or were now properly consumed. Verified that the escapes were properly terminated at a generic level. Verified that the Notcurses tools continued to show expected output on the Linux console, except now without escape bleedthrough. Link: https://lore.kernel.org/lkml/YSydL0q8iaUfkphg@schwarzgerat.orthanc/ Signed-off-by: nick black Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Tetsuo Handa Cc: Daniel Vetter Signed-off-by: Linus Torvalds --- drivers/tty/vt/vt.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index ef981d3b7bb4..cb72393f92d3 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2059,7 +2059,7 @@ static void restore_cur(struct vc_data *vc) enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd, - ESpalette, ESosc }; + ESpalette, ESosc, ESapc, ESpm, ESdcs }; /* console_lock is held (except via vc_init()) */ static void reset_terminal(struct vc_data *vc, int do_clear) @@ -2133,20 +2133,28 @@ static void vc_setGx(struct vc_data *vc, unsigned int which, int c) vc->vc_translate = set_translate(*charset, vc); } +/* is this state an ANSI control string? */ +static bool ansi_control_string(unsigned int state) +{ + if (state == ESosc || state == ESapc || state == ESpm || state == ESdcs) + return true; + return false; +} + /* console_lock is held */ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) { /* * Control characters can be used in the _middle_ - * of an escape sequence. + * of an escape sequence, aside from ANSI control strings. */ - if (vc->vc_state == ESosc && c>=8 && c<=13) /* ... except for OSC */ + if (ansi_control_string(vc->vc_state) && c >= 8 && c <= 13) return; switch (c) { case 0: return; case 7: - if (vc->vc_state == ESosc) + if (ansi_control_string(vc->vc_state)) vc->vc_state = ESnormal; else if (vc->vc_bell_duration) kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); @@ -2207,6 +2215,12 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) case ']': vc->vc_state = ESnonstd; return; + case '_': + vc->vc_state = ESapc; + return; + case '^': + vc->vc_state = ESpm; + return; case '%': vc->vc_state = ESpercent; return; @@ -2224,6 +2238,9 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) if (vc->state.x < VC_TABSTOPS_COUNT) set_bit(vc->state.x, vc->vc_tab_stop); return; + case 'P': + vc->vc_state = ESdcs; + return; case 'Z': respond_ID(tty); return; @@ -2520,8 +2537,14 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) vc_setGx(vc, 1, c); vc->vc_state = ESnormal; return; + case ESapc: + return; case ESosc: return; + case ESpm: + return; + case ESdcs: + return; default: vc->vc_state = ESnormal; } From 2287a51ba822384834dafc1c798453375d1107c7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 30 Aug 2021 08:55:18 -0700 Subject: [PATCH 177/177] vt_kdsetmode: extend console locking As per the long-suffering comment. Reported-by: Minh Yuan Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Linus Torvalds --- drivers/tty/vt/vt_ioctl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 0e0cd9e9e589..3639bb6dc372 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -246,6 +246,8 @@ int vt_waitactive(int n) * * XXX It should at least call into the driver, fbdev's definitely need to * restore their engine state. --BenH + * + * Called with the console lock held. */ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) { @@ -262,7 +264,6 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) return -EINVAL; } - /* FIXME: this needs the console lock extending */ if (vc->vc_mode == mode) return 0; @@ -271,12 +272,10 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) return 0; /* explicitly blank/unblank the screen if switching modes */ - console_lock(); if (mode == KD_TEXT) do_unblank_screen(1); else do_blank_screen(1); - console_unlock(); return 0; } @@ -378,7 +377,10 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, if (!perm) return -EPERM; - return vt_kdsetmode(vc, arg); + console_lock(); + ret = vt_kdsetmode(vc, arg); + console_unlock(); + return ret; case KDGETMODE: return put_user(vc->vc_mode, (int __user *)arg);