From patchwork Sat Jan 9 13:29:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008283 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5F508C43333 for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3AE3123AA7 for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726510AbhAINbO (ORCPT ); Sat, 9 Jan 2021 08:31:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32772 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726522AbhAINbN (ORCPT ); Sat, 9 Jan 2021 08:31:13 -0500 Received: from relay02.th.seeweb.it (relay02.th.seeweb.it [IPv6:2001:4b7a:2000:18::163]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7EF2FC0617B0 for ; Sat, 9 Jan 2021 05:30:27 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id 08DBB1F562; Sat, 9 Jan 2021 14:29:44 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 1/7] regulator: qcom-labibb: Implement voltage selector ops Date: Sat, 9 Jan 2021 14:29:15 +0100 Message-Id: <20210109132921.140932-2-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Implement {get,set}_voltage_sel, list_voltage, map_voltage with the useful regulator regmap helpers in order to be able to manage the voltage of LAB (positive) and IBB (negative) regulators. In particular, the supported ranges are the following: - LAB (pos): 4600mV to 6100mV with 100mV stepping, - IBB (neg): -7700mV to -1400mV with 100mV stepping. Signed-off-by: AngeloGioacchino Del Regno --- drivers/regulator/qcom-labibb-regulator.c | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 8ccf572394a2..9f51c96f16fb 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -19,6 +19,12 @@ #define PMI8998_IBB_REG_BASE 0xdc00 #define REG_LABIBB_STATUS1 0x08 + +#define REG_LABIBB_VOLTAGE 0x41 + #define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7) + #define LAB_VOLTAGE_SET_MASK GENMASK(3, 0) + #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) + #define REG_LABIBB_ENABLE_CTL 0x46 #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) @@ -51,6 +57,10 @@ static const struct regulator_ops qcom_labibb_ops = { .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, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -59,9 +69,18 @@ static const struct regulator_desc pmi8998_lab_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = LAB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), + .vsel_mask = LAB_VOLTAGE_SET_MASK, + .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), + .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(4600000, 0, 15, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 16, .ops = &qcom_labibb_ops, }; @@ -71,9 +90,18 @@ static const struct regulator_desc pmi8998_ibb_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = IBB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), + .vsel_mask = IBB_VOLTAGE_SET_MASK, + .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), + .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1400000, 0, 63, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 64, .ops = &qcom_labibb_ops, }; From patchwork Sat Jan 9 13:29:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008287 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 01274C433DB for ; Sat, 9 Jan 2021 13:31:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CD6E3239E4 for ; Sat, 9 Jan 2021 13:31:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726518AbhAINb0 (ORCPT ); Sat, 9 Jan 2021 08:31:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60982 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725901AbhAINbZ (ORCPT ); Sat, 9 Jan 2021 08:31:25 -0500 Received: from relay01.th.seeweb.it (relay01.th.seeweb.it [IPv6:2001:4b7a:2000:18::162]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 446AEC0617A4 for ; Sat, 9 Jan 2021 05:29:50 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id 61DD21F568; Sat, 9 Jan 2021 14:29:44 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 2/7] regulator: qcom-labibb: Implement current limiting Date: Sat, 9 Jan 2021 14:29:16 +0100 Message-Id: <20210109132921.140932-3-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org LAB and IBB regulators can be current-limited by setting the appropriate registers, but this operation is granted only after sending an unlock code for secure access. Besides the secure access, it would be possible to use the regmap helper for get_current_limit, as there is no security blocking reads, but I chose not to as to avoid having a very big array containing current limits, especially for IBB. That said, these regulators support current limiting for: - LAB (pos): 200-1600mA, with 200mA per step (8 steps), - IBB (neg): 0-1550mA, with 50mA per step (32 steps). Signed-off-by: AngeloGioacchino Del Regno --- drivers/regulator/qcom-labibb-regulator.c | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 9f51c96f16fb..c93d0d51cecb 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -29,6 +29,15 @@ #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) +#define REG_LABIBB_CURRENT_LIMIT 0x4b + #define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0) + #define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0) + #define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3) + #define LABIBB_CURRENT_LIMIT_EN BIT(7) + +#define REG_LABIBB_SEC_ACCESS 0xd0 + #define LABIBB_SEC_UNLOCK_CODE 0xa5 + #define LAB_ENABLE_CTL_MASK BIT(7) #define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6)) @@ -37,11 +46,18 @@ #define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10) #define LABIBB_POLL_ENABLED_TIME 1000 +struct labibb_current_limits { + u32 uA_min; + u32 uA_step; + u8 ovr_val; +}; + struct labibb_regulator { struct regulator_desc desc; struct device *dev; struct regmap *regmap; struct regulator_dev *rdev; + struct labibb_current_limits uA_limits; u16 base; u8 type; }; @@ -53,6 +69,56 @@ struct labibb_regulator_data { const struct regulator_desc *desc; }; +static int qcom_labibb_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + struct regulator_desc *desc = &vreg->desc; + struct labibb_current_limits *lim = &vreg->uA_limits; + u32 mask, val; + int i, ret, sel = -1; + + if (min_uA < lim->uA_min || max_uA < lim->uA_min) + return -EINVAL; + + for (i = 0; i < desc->n_current_limits; i++) { + int uA_limit = (lim->uA_step * i) + lim->uA_min; + if (max_uA >= uA_limit && min_uA <= uA_limit) + sel = i; + } + if (sel < 0) + return -EINVAL; + + /* Current limit setting needs secure access */ + ret = regmap_write(vreg->regmap, vreg->base + REG_LABIBB_SEC_ACCESS, + LABIBB_SEC_UNLOCK_CODE); + if (ret) + return ret; + + mask = desc->csel_mask | lim->ovr_val; + mask |= LABIBB_CURRENT_LIMIT_EN; + val = (u32)sel | lim->ovr_val; + val |= LABIBB_CURRENT_LIMIT_EN; + + return regmap_update_bits(vreg->regmap, desc->csel_reg, mask, val); + } + +static int qcom_labibb_get_current_limit(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + struct regulator_desc *desc = &vreg->desc; + struct labibb_current_limits *lim = &vreg->uA_limits; + unsigned int cur_step; + int ret; + + ret = regmap_read(vreg->regmap, desc->csel_reg, &cur_step); + if (ret) + return ret; + cur_step &= desc->csel_mask; + + return (cur_step * lim->uA_step) + lim->uA_min; +} + static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -61,6 +127,8 @@ static const struct regulator_ops qcom_labibb_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, + .set_current_limit = qcom_labibb_set_current_limit, + .get_current_limit = qcom_labibb_get_current_limit, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -73,6 +141,9 @@ static const struct regulator_desc pmi8998_lab_desc = { .vsel_mask = LAB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, + .csel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_CURRENT_LIMIT), + .csel_mask = LAB_CURRENT_LIMIT_MASK, + .n_current_limits = 8, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, @@ -94,6 +165,9 @@ static const struct regulator_desc pmi8998_ibb_desc = { .vsel_mask = IBB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, + .csel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_CURRENT_LIMIT), + .csel_mask = IBB_CURRENT_LIMIT_MASK, + .n_current_limits = 32, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, @@ -167,6 +241,18 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) vreg->base = reg_data->base; vreg->type = reg_data->type; + if (type == QCOM_LAB_TYPE) { + /* LAB Limits: 200-1600mA */ + vreg->uA_limits.uA_min = 200000; + vreg->uA_limits.uA_step = 200000; + vreg->uA_limits.ovr_val = LAB_CURRENT_LIMIT_OVERRIDE_EN; + } else { + /* IBB Limits: 0-1550mA */ + vreg->uA_limits.uA_min = 0; + vreg->uA_limits.uA_step = 50000; + vreg->uA_limits.ovr_val = 0; /* No override bit */ + } + memcpy(&vreg->desc, reg_data->desc, sizeof(vreg->desc)); vreg->desc.of_match = reg_data->name; vreg->desc.name = reg_data->name; From patchwork Sat Jan 9 13:29:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008285 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6741CC4332E for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5155A239E4 for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726516AbhAINbN (ORCPT ); Sat, 9 Jan 2021 08:31:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60992 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725937AbhAINbM (ORCPT ); Sat, 9 Jan 2021 08:31:12 -0500 Received: from relay02.th.seeweb.it (relay02.th.seeweb.it [IPv6:2001:4b7a:2000:18::163]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 523A4C0617A6; Sat, 9 Jan 2021 05:29:50 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id B6FCD1F3F6; Sat, 9 Jan 2021 14:29:44 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 3/7] regulator: qcom-labibb: Implement pull-down, softstart, active discharge Date: Sat, 9 Jan 2021 14:29:17 +0100 Message-Id: <20210109132921.140932-4-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Soft start is required to avoid inrush current during LAB ramp-up and IBB ramp-down, protecting connected hardware to which we supply voltage. Since soft start is configurable on both LAB and IBB regulators, it was necessary to add two DT properties, respectively "qcom,soft-start-us" to control LAB ramp-up and "qcom,discharge-resistor-kohms" to control the discharge resistor for IBB ramp-down, which obviously brought the need of implementing a of_parse callback for both regulators. Finally, also implement pull-down mode in order to avoid unpredictable behavior when the regulators are disabled (random voltage spikes etc). Signed-off-by: AngeloGioacchino Del Regno --- drivers/regulator/qcom-labibb-regulator.c | 94 +++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index c93d0d51cecb..21175e297c1f 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -29,12 +29,23 @@ #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) +#define REG_LABIBB_PD_CTL 0x47 + #define LAB_PD_CTL_MASK GENMASK(1, 0) + #define IBB_PD_CTL_MASK (BIT(0) | BIT(7)) + #define LAB_PD_CTL_STRONG_PULL BIT(0) + #define IBB_PD_CTL_HALF_STRENGTH BIT(0) + #define IBB_PD_CTL_EN BIT(7) + #define REG_LABIBB_CURRENT_LIMIT 0x4b #define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0) #define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0) #define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3) #define LABIBB_CURRENT_LIMIT_EN BIT(7) +#define REG_IBB_PWRUP_PWRDN_CTL_1 0x58 + #define IBB_CTL_1_DISCHARGE_EN BIT(2) + +#define REG_LABIBB_SOFT_START_CTL 0x5f #define REG_LABIBB_SEC_ACCESS 0xd0 #define LABIBB_SEC_UNLOCK_CODE 0xa5 @@ -60,6 +71,8 @@ struct labibb_regulator { struct labibb_current_limits uA_limits; u16 base; u8 type; + u8 dischg_sel; + u8 soft_start_sel; }; struct labibb_regulator_data { @@ -119,6 +132,70 @@ static int qcom_labibb_get_current_limit(struct regulator_dev *rdev) return (cur_step * lim->uA_step) + lim->uA_min; } +static int qcom_labibb_set_soft_start(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + u32 val = 0; + + if (vreg->type == QCOM_IBB_TYPE) + val = vreg->dischg_sel; + else + val = vreg->soft_start_sel; + + return regmap_write(rdev->regmap, rdev->desc->soft_start_reg, val); +} + +static int qcom_labibb_get_table_sel(const int *table, int sz, u32 value) +{ + int i; + + for (i = 0; i < sz; i++) + if (table[i] == value) + return i; + return -EINVAL; +} + +/* IBB discharge resistor values in KOhms */ +static const int dischg_resistor_values[] = { 300, 64, 32, 16 }; + +/* Soft start time in microseconds */ +static const int soft_start_values[] = { 200, 400, 600, 800 }; + +static int qcom_labibb_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct labibb_regulator *vreg = config->driver_data; + u32 dischg_kohms, soft_start_time; + int ret; + + ret = of_property_read_u32(np, "qcom,discharge-resistor-kohms", + &dischg_kohms); + if (ret) + dischg_kohms = 300; + + ret = qcom_labibb_get_table_sel(dischg_resistor_values, + ARRAY_SIZE(dischg_resistor_values), + dischg_kohms); + if (ret < 0) + return ret; + vreg->dischg_sel = (u8)ret; + + ret = of_property_read_u32(np, "qcom,soft-start-us", + &soft_start_time); + if (ret) + soft_start_time = 200; + + ret = qcom_labibb_get_table_sel(soft_start_values, + ARRAY_SIZE(soft_start_values), + soft_start_time); + if (ret < 0) + return ret; + vreg->soft_start_sel = (u8)ret; + + return 0; +} + static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -127,8 +204,11 @@ static const struct regulator_ops qcom_labibb_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_pull_down = regulator_set_pull_down_regmap, .set_current_limit = qcom_labibb_set_current_limit, .get_current_limit = qcom_labibb_get_current_limit, + .set_soft_start = qcom_labibb_set_soft_start, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -137,6 +217,10 @@ static const struct regulator_desc pmi8998_lab_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = LAB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .soft_start_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_SOFT_START_CTL), + .pull_down_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_PD_CTL), + .pull_down_mask = LAB_PD_CTL_MASK, + .pull_down_val_on = LAB_PD_CTL_STRONG_PULL, .vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), .vsel_mask = LAB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), @@ -153,6 +237,7 @@ static const struct regulator_desc pmi8998_lab_desc = { .n_linear_ranges = 1, .n_voltages = 16, .ops = &qcom_labibb_ops, + .of_parse_cb = qcom_labibb_of_parse_cb, }; static const struct regulator_desc pmi8998_ibb_desc = { @@ -161,6 +246,14 @@ static const struct regulator_desc pmi8998_ibb_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = IBB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .soft_start_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_SOFT_START_CTL), + .active_discharge_off = 0, + .active_discharge_on = IBB_CTL_1_DISCHARGE_EN, + .active_discharge_mask = IBB_CTL_1_DISCHARGE_EN, + .active_discharge_reg = (PMI8998_IBB_REG_BASE + REG_IBB_PWRUP_PWRDN_CTL_1), + .pull_down_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_PD_CTL), + .pull_down_mask = IBB_PD_CTL_MASK, + .pull_down_val_on = IBB_PD_CTL_HALF_STRENGTH | IBB_PD_CTL_EN, .vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), .vsel_mask = IBB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), @@ -177,6 +270,7 @@ static const struct regulator_desc pmi8998_ibb_desc = { .n_linear_ranges = 1, .n_voltages = 64, .ops = &qcom_labibb_ops, + .of_parse_cb = qcom_labibb_of_parse_cb, }; static const struct labibb_regulator_data pmi8998_labibb_data[] = { From patchwork Sat Jan 9 13:29:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008277 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B99A7C433E0 for ; Sat, 9 Jan 2021 13:30:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 72E0523A1C for ; Sat, 9 Jan 2021 13:30:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725826AbhAINad (ORCPT ); Sat, 9 Jan 2021 08:30:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60830 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725839AbhAINaa (ORCPT ); Sat, 9 Jan 2021 08:30:30 -0500 Received: from relay04.th.seeweb.it (relay04.th.seeweb.it [IPv6:2001:4b7a:2000:18::165]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3D74AC0617A3 for ; Sat, 9 Jan 2021 05:29:50 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id 19D7A1F569; Sat, 9 Jan 2021 14:29:45 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 4/7] dt-bindings: regulator: qcom-labibb: Document soft start properties Date: Sat, 9 Jan 2021 14:29:18 +0100 Message-Id: <20210109132921.140932-5-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Document properties to configure soft start and discharge resistor for LAB and IBB respectively. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/regulator/qcom-labibb-regulator.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml index 53853ec20fe2..1cdaff66fdb9 100644 --- a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml @@ -22,6 +22,10 @@ properties: type: object properties: + qcom,soft-start-us: + description: Regulator soft start time in microseconds. + enum: [200, 400, 600, 800] + default: 200 interrupts: maxItems: 1 @@ -35,6 +39,10 @@ properties: type: object properties: + qcom,discharge-resistor-kohms: + description: Discharge resistor value in KiloOhms. + enum: [300, 64, 32, 16] + default: 300 interrupts: maxItems: 1 From patchwork Sat Jan 9 13:29:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008281 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 13AE1C4332D for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DCE79239E4 for ; Sat, 9 Jan 2021 13:31:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726504AbhAINbL (ORCPT ); Sat, 9 Jan 2021 08:31:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60980 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725937AbhAINbK (ORCPT ); Sat, 9 Jan 2021 08:31:10 -0500 Received: from relay04.th.seeweb.it (relay04.th.seeweb.it [IPv6:2001:4b7a:2000:18::165]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 35C3EC0617A2; Sat, 9 Jan 2021 05:29:50 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id 702211F56E; Sat, 9 Jan 2021 14:29:45 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 5/7] regulator: qcom-labibb: Implement short-circuit and over-current IRQs Date: Sat, 9 Jan 2021 14:29:19 +0100 Message-Id: <20210109132921.140932-6-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are very important for regulators like LAB and IBB, which are designed to provide from very small to relatively big amounts of current to the device (normally, a display). Now that this regulator supports both voltage setting and current limiting in this driver, to me it looked like being somehow essential to provide support for SCP and OCP, for two reasons: 1. SCP is a drastic measure to prevent damaging "more" hardware in the worst situations, if any was damaged, preventing potentially drastic issues; 2. OCP is a great way to protect the hardware that we're powering through these regulators as if anything bad happens, the HW will draw more current than expected: in this case, the OCP interrupt will fire and the regulators will be immediately shut down, preventing hardware damage in many cases. Both interrupts were successfully tested in a "sort-of" controlled manner, with the following methodology: Short-Circuit Protection (SCP): 1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA; 2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces on a FxTec Pro1 smartphone for a very brief time (in short words, "just a rapid touch with flying wires"); 3. The Short-Circuit protection trips: IRQ raises, regulators get cut. Recovery OK, test repeated without rebooting, OK. Over-Current Protection (OCP): 1. Set LAB/IBB to the expected voltage to power up the display of a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set current limit to LAB 200mA, IBB 50mA (the values that this display unit needs are 200/800mA); 2. Boot the kernel: OCP fires. Recovery never happens because the selected current limit is too low, but that's expected. Test OK. 3. Set LAB/IBB to the expected current limits for XZ Premium (LAB 200mA, IBB 800mA), but lower than expected voltage, specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V); 4. Boot the kernel: OCP fires. Recovery never happens because the selected voltage (still in the working range limits) is producing a current draw of more than 200mA on LAB. Test OK. Signed-off-by: AngeloGioacchino Del Regno --- drivers/regulator/qcom-labibb-regulator.c | 430 +++++++++++++++++++++- 1 file changed, 427 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 21175e297c1f..413506481a08 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -17,8 +17,20 @@ #define PMI8998_LAB_REG_BASE 0xde00 #define PMI8998_IBB_REG_BASE 0xdc00 +#define PMI8998_IBB_LAB_REG_OFFSET 0x200 #define REG_LABIBB_STATUS1 0x08 + #define LABIBB_STATUS1_SC_BIT BIT(6) + #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) + +#define REG_LABIBB_INT_SET_TYPE 0x11 +#define REG_LABIBB_INT_POLARITY_HIGH 0x12 +#define REG_LABIBB_INT_POLARITY_LOW 0x13 +#define REG_LABIBB_INT_LATCHED_CLR 0x14 +#define REG_LABIBB_INT_EN_SET 0x15 +#define REG_LABIBB_INT_EN_CLR 0x16 + #define LABIBB_INT_VREG_OK BIT(0) + #define LABIBB_INT_VREG_TYPE_LEVEL 0 #define REG_LABIBB_VOLTAGE 0x41 #define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7) @@ -26,8 +38,7 @@ #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) #define REG_LABIBB_ENABLE_CTL 0x46 -#define LABIBB_STATUS1_VREG_OK_BIT BIT(7) -#define LABIBB_CONTROL_ENABLE BIT(7) + #define LABIBB_CONTROL_ENABLE BIT(7) #define REG_LABIBB_PD_CTL 0x47 #define LAB_PD_CTL_MASK GENMASK(1, 0) @@ -56,6 +67,11 @@ #define LAB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 2) #define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10) #define LABIBB_POLL_ENABLED_TIME 1000 +#define OCP_RECOVERY_INTERVAL_MS 500 +#define SC_RECOVERY_INTERVAL_MS 250 +#define LABIBB_MAX_OCP_COUNT 4 +#define LABIBB_MAX_SC_COUNT 3 +#define LABIBB_MAX_FATAL_COUNT 2 struct labibb_current_limits { u32 uA_min; @@ -69,10 +85,18 @@ struct labibb_regulator { struct regmap *regmap; struct regulator_dev *rdev; struct labibb_current_limits uA_limits; + struct delayed_work ocp_recovery_work; + struct delayed_work sc_recovery_work; u16 base; u8 type; u8 dischg_sel; u8 soft_start_sel; + int sc_irq; + int sc_count; + int ocp_irq; + int ocp_irq_count; + int fatal_count; + bool ocp_irq_requested; }; struct labibb_regulator_data { @@ -82,6 +106,376 @@ struct labibb_regulator_data { const struct regulator_desc *desc; }; +static int qcom_labibb_ocp_hw_enable(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + int ret; + + /* Clear irq latch status to avoid spurious event */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_LATCHED_CLR, + LABIBB_INT_VREG_OK, 1); + if (ret) + return ret; + + /* Enable OCP HW interrupt */ + return regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_EN_SET, + LABIBB_INT_VREG_OK, 1); +} + +static int qcom_labibb_ocp_hw_disable(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + + return regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_EN_CLR, + LABIBB_INT_VREG_OK, 1); +} + +/* + * qcom_labibb_check_ocp_status - Check the Over-Current Protection status + * @rdev: Regulator device + * + * This function checks the STATUS1 register for the VREG_OK bit: if it is + * set, then there is no Over-Current event. + * + * Returns: Zero if there is no over-current, 1 if in over-current or + * negative number for error + */ +static int qcom_labibb_check_ocp_status(struct labibb_regulator *vreg) +{ + u32 cur_status; + int ret; + + ret = regmap_read(vreg->rdev->regmap, vreg->base + REG_LABIBB_STATUS1, + &cur_status); + if (ret) + return ret; + + return !(cur_status & LABIBB_STATUS1_VREG_OK_BIT); +} + +static void qcom_labibb_ocp_recovery_worker(struct work_struct *work) +{ + struct labibb_regulator *vreg; + const struct regulator_ops *ops; + int ret; + + vreg = container_of(work, struct labibb_regulator, + ocp_recovery_work.work); + ops = vreg->rdev->desc->ops; + + if (vreg->ocp_irq_count >= LABIBB_MAX_OCP_COUNT) { + /* + * If we tried to disable the regulator multiple times but + * we kept failing, there's only one last hope to save our + * hardware from the death: raise a kernel bug, reboot and + * hope that the bootloader kindly saves us. This, though + * is done only as paranoid checking, because failing the + * regmap write to disable the vreg is almost impossible, + * since we got here after multiple regmap R/W. + */ + BUG_ON(vreg->fatal_count > LABIBB_MAX_FATAL_COUNT); + dev_err(&vreg->rdev->dev, "LABIBB: CRITICAL: Disabling regulator\n"); + + /* Disable the regulator immediately to avoid damage */ + ret = ops->disable(vreg->rdev); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + enable_irq(vreg->ocp_irq); + vreg->fatal_count = 0; + return; + } + + ret = qcom_labibb_check_ocp_status(vreg); + if (ret != 0) { + vreg->ocp_irq_count++; + goto reschedule; + } + + ret = qcom_labibb_ocp_hw_enable(vreg->rdev); + if (ret) { + /* We cannot trust it without OCP enabled. */ + dev_err(vreg->dev, "Cannot enable OCP IRQ\n"); + vreg->ocp_irq_count++; + goto reschedule; + } + + enable_irq(vreg->ocp_irq); + /* Everything went fine: reset the OCP count! */ + vreg->ocp_irq_count = 0; + return; + +reschedule: + mod_delayed_work(system_wq, &vreg->ocp_recovery_work, + msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS)); +} + +static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) +{ + struct labibb_regulator *vreg = chip; + const struct regulator_ops *ops = vreg->rdev->desc->ops; + int ret; + + /* If the regulator is not enabled, this is a fake event */ + if (!ops->is_enabled(vreg->rdev)) + return 0; + + /* If we tried to recover for too many times it's not getting better */ + if (vreg->ocp_irq_count > LABIBB_MAX_OCP_COUNT) + return IRQ_NONE; + + /* + * If we (unlikely) can't read this register, to prevent hardware + * damage at all costs, we assume that the overcurrent event was + * real; Moreover, if the status register is not signaling OCP, + * it was a spurious event, so it's all ok. + */ + ret = qcom_labibb_check_ocp_status(vreg); + if (ret == 0) { + vreg->ocp_irq_count = 0; + goto end; + } + vreg->ocp_irq_count++; + + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq(irq); + + /* Warn the user for overcurrent */ + dev_warn(vreg->dev, "Over-Current interrupt fired!\n"); + + /* Disable the interrupt to avoid hogging */ + ret = qcom_labibb_ocp_hw_disable(vreg->rdev); + if (ret) + goto end; + + /* Signal overcurrent event to drivers */ + regulator_notifier_call_chain(vreg->rdev, + REGULATOR_EVENT_OVER_CURRENT, NULL); + +end: + /* Schedule the recovery work */ + schedule_delayed_work(&vreg->ocp_recovery_work, + msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS)); + return ret ? IRQ_NONE : IRQ_HANDLED; +} + +static int qcom_labibb_set_ocp(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + char *ocp_irq_name; + u32 irq_flags = IRQF_ONESHOT; + int irq_trig_low, ret; + + /* If there is no OCP interrupt, there's nothing to set */ + if (vreg->ocp_irq <= 0) + return -EINVAL; + + /* This function should be called only once, anyway. */ + if (unlikely(vreg->ocp_irq_requested)) + return 0; + vreg->ocp_irq_requested = true; + + ocp_irq_name = devm_kasprintf(vreg->dev, GFP_KERNEL, "%s-over-current", + vreg->desc.name); + if (!ocp_irq_name) + return -ENOMEM; + + /* IRQ polarities - LAB: trigger-low, IBB: trigger-high */ + if (vreg->type == QCOM_LAB_TYPE) { + irq_flags |= IRQF_TRIGGER_LOW; + irq_trig_low = 1; + } else { + irq_flags |= IRQF_TRIGGER_HIGH; + irq_trig_low = 0; + } + + /* Activate OCP HW level interrupt */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_SET_TYPE, + LABIBB_INT_VREG_OK, + LABIBB_INT_VREG_TYPE_LEVEL); + if (ret) + return ret; + + /* Set OCP interrupt polarity */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_POLARITY_HIGH, + LABIBB_INT_VREG_OK, !irq_trig_low); + if (ret) + return ret; + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_POLARITY_LOW, + LABIBB_INT_VREG_OK, irq_trig_low); + if (ret) + return ret; + + ret = qcom_labibb_ocp_hw_enable(rdev); + if (ret) + return ret; + + return devm_request_threaded_irq(vreg->dev, vreg->ocp_irq, NULL, + qcom_labibb_ocp_isr, irq_flags, + ocp_irq_name, vreg); +} + +/* + * qcom_labibb_check_sc_status - Check the Short Circuit Protection status + * @rdev: Regulator device + * + * This function checks the STATUS1 register on both LAB and IBB regulators + * for the ShortCircuit bit: if it is set on *any* of them, then we have + * experienced a short-circuit event. + * + * Returns: Zero if there is no short-circuit, 1 if in short-circuit or + * negative number for error + */ +static int qcom_labibb_check_sc_status(struct labibb_regulator *vreg) +{ + u32 ibb_status, ibb_reg, lab_status, lab_reg; + int ret; + + /* We have to work on both regulators due to PBS... */ + lab_reg = ibb_reg = vreg->base; + if (vreg->type == QCOM_LAB_TYPE) + ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET; + else + lab_reg += PMI8998_IBB_LAB_REG_OFFSET; + + ret = regmap_read(vreg->rdev->regmap, lab_reg, &lab_status); + if (ret) + return ret; + ret = regmap_read(vreg->rdev->regmap, ibb_reg, &ibb_status); + if (ret) + return ret; + + return !!(lab_status & LABIBB_STATUS1_SC_BIT) || + !!(ibb_status & LABIBB_STATUS1_SC_BIT); +} + +static void qcom_labibb_sc_recovery_worker(struct work_struct *work) +{ + struct labibb_regulator *vreg; + const struct regulator_ops *ops; + u32 lab_reg, ibb_reg, temp, val; + bool pbs_cut = false; + int i, sc, ret; + + vreg = container_of(work, struct labibb_regulator, + sc_recovery_work.work); + ops = vreg->rdev->desc->ops; + + /* + * If we tried to check the regulator status multiple times but we + * kept failing, then just bail out, as the Portable Batch System + * (PBS) will disable the vregs for us, preventing hardware damage. + */ + if (vreg->fatal_count > LABIBB_MAX_FATAL_COUNT) + return; + + /* Too many short-circuit events. Throw in the towel. */ + if (vreg->sc_count > LABIBB_MAX_SC_COUNT) + return; + + /* + * The Portable Batch System (PBS) automatically disables LAB + * and IBB when a short-circuit event is detected, so we have to + * check and work on both of them at the same time. + */ + lab_reg = ibb_reg = vreg->base; + if (vreg->type == QCOM_LAB_TYPE) + ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET; + else + lab_reg += PMI8998_IBB_LAB_REG_OFFSET; + + sc = qcom_labibb_check_sc_status(vreg); + if (sc) + goto reschedule; + + for (i = 0; i < LABIBB_MAX_SC_COUNT; i++) { + ret = regmap_read(vreg->regmap, lab_reg, &temp); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + val = temp; + + ret = regmap_read(vreg->regmap, ibb_reg, &temp); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + val &= temp; + + if (!!(val & LABIBB_CONTROL_ENABLE)) { + usleep_range(5000, 6000); + continue; + } + pbs_cut = true; + break; + } + if (pbs_cut) + goto reschedule; + + /* + * If we have reached this point, we either had a spurious SC IRQ + * or we have successfully recovered from the SC condition, which + * means that we can re-enable the regulators, if they have ever + * been disabled by the PBS. + */ + ret = ops->enable(vreg->rdev); + if (ret) + goto reschedule; + + /* Everything went fine: reset the OCP count! */ + vreg->sc_count = 0; + enable_irq(vreg->sc_irq); + return; + +reschedule: + /* + * Now that we have done basic handling of the short-circuit, + * reschedule this worker in the regular system workqueue, as + * taking action is not truly urgent anymore. + */ + vreg->sc_count++; + mod_delayed_work(system_wq, &vreg->sc_recovery_work, + msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS)); +} + +static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) +{ + struct labibb_regulator *vreg = chip; + + if (vreg->sc_count > LABIBB_MAX_SC_COUNT) + return IRQ_NONE; + + /* Warn the user for short circuit */ + dev_warn(vreg->dev, "Short-Circuit interrupt fired!\n"); + + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq(irq); + + /* Signal out of regulation event to drivers */ + regulator_notifier_call_chain(vreg->rdev, + REGULATOR_EVENT_REGULATION_OUT, NULL); + + /* Schedule the short-circuit handling as high-priority work */ + mod_delayed_work(system_highpri_wq, &vreg->sc_recovery_work, + msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS)); + return IRQ_HANDLED; +} + + static int qcom_labibb_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { @@ -166,8 +560,37 @@ static int qcom_labibb_of_parse_cb(struct device_node *np, struct regulator_config *config) { struct labibb_regulator *vreg = config->driver_data; + char *sc_irq_name; u32 dischg_kohms, soft_start_time; - int ret; + int irq, ret; + + /* The Short Circuit interrupt is critical: fail if not found */ + irq = of_irq_get(np, 0); + if (irq <= 0) + return dev_err_probe(vreg->dev, (irq == 0 ? -EINVAL : irq), + "Short-circuit interrupt not found.\n"); + vreg->sc_irq = irq; + INIT_DELAYED_WORK(&vreg->sc_recovery_work, + qcom_labibb_sc_recovery_worker); + + sc_irq_name = devm_kasprintf(vreg->dev, GFP_KERNEL, "%s-short-circuit", + vreg->desc.name); + if (!sc_irq_name) + return -ENOMEM; + + ret = devm_request_threaded_irq(vreg->dev, vreg->sc_irq, NULL, + qcom_labibb_sc_isr, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + sc_irq_name, vreg); + if (ret) + return ret; + + /* OverCurrent Protection IRQ is optional */ + irq = of_irq_get(np, 1); + vreg->ocp_irq = irq; + vreg->ocp_irq_count = 0; + INIT_DELAYED_WORK(&vreg->ocp_recovery_work, + qcom_labibb_ocp_recovery_worker); ret = of_property_read_u32(np, "qcom,discharge-resistor-kohms", &dischg_kohms); @@ -209,6 +632,7 @@ static const struct regulator_ops qcom_labibb_ops = { .set_current_limit = qcom_labibb_set_current_limit, .get_current_limit = qcom_labibb_get_current_limit, .set_soft_start = qcom_labibb_set_soft_start, + .set_over_current_protection = qcom_labibb_set_ocp, }; static const struct regulator_desc pmi8998_lab_desc = { From patchwork Sat Jan 9 13:29:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008289 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9DD4CC43381 for ; Sat, 9 Jan 2021 13:31:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6B465239E4 for ; Sat, 9 Jan 2021 13:31:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725906AbhAINb2 (ORCPT ); Sat, 9 Jan 2021 08:31:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60992 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725896AbhAINb1 (ORCPT ); Sat, 9 Jan 2021 08:31:27 -0500 Received: from m-r1.th.seeweb.it (m-r1.th.seeweb.it [IPv6:2001:4b7a:2000:18::170]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A9A24C0617BA for ; Sat, 9 Jan 2021 05:30:43 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id C83361F570; Sat, 9 Jan 2021 14:29:45 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 6/7] dt-bindings: regulator: qcom-labibb: Document SCP/OCP interrupts Date: Sat, 9 Jan 2021 14:29:20 +0100 Message-Id: <20210109132921.140932-7-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are now implemented in the driver: document the interrupts. This also fixes wrong documentation about the SCP interrupt for LAB. Signed-off-by: AngeloGioacchino Del Regno --- .../regulator/qcom-labibb-regulator.yaml | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml index 1cdaff66fdb9..8cf883d78a45 100644 --- a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml @@ -28,9 +28,10 @@ properties: default: 200 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - Short-circuit interrupt for lab. + Short-circuit and over-current interrupts for lab. required: - interrupts @@ -45,9 +46,10 @@ properties: default: 300 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - Short-circuit interrupt for lab. + Short-circuit and over-current interrupts for ibb. required: - interrupts @@ -65,13 +67,15 @@ examples: compatible = "qcom,pmi8998-lab-ibb"; lab { - interrupts = <0x3 0x0 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "sc-err"; + interrupts = <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>, + <0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "sc-err", "ocp"; }; ibb { - interrupts = <0x3 0x2 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "sc-err"; + interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>, + <0x3 0xdc 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "sc-err", "ocp"; }; }; From patchwork Sat Jan 9 13:29:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AngeloGioacchino Del Regno X-Patchwork-Id: 12008279 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2B241C43331 for ; Sat, 9 Jan 2021 13:31:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F075623A7C for ; Sat, 9 Jan 2021 13:31:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725937AbhAINbN (ORCPT ); Sat, 9 Jan 2021 08:31:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60994 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726510AbhAINbM (ORCPT ); Sat, 9 Jan 2021 08:31:12 -0500 Received: from relay01.th.seeweb.it (relay01.th.seeweb.it [IPv6:2001:4b7a:2000:18::162]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 24DAEC0617A9; Sat, 9 Jan 2021 05:29:52 -0800 (PST) Received: from IcarusMOD.eternityproject.eu (unknown [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by m-r1.th.seeweb.it (Postfix) with ESMTPSA id 2AFB91F571; Sat, 9 Jan 2021 14:29:46 +0100 (CET) From: AngeloGioacchino Del Regno To: linux-arm-msm@vger.kernel.org Cc: agross@kernel.org, bjorn.andersson@linaro.org, lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, sumit.semwal@linaro.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, phone-devel@vger.kernel.org, konrad.dybcio@somainline.org, marijn.suijten@somainline.org, martin.botka@somainline.org, AngeloGioacchino Del Regno Subject: [PATCH 7/7] arm64: dts: pmi8998: Add the right interrupts for LAB/IBB SCP and OCP Date: Sat, 9 Jan 2021 14:29:21 +0100 Message-Id: <20210109132921.140932-8-angelogioacchino.delregno@somainline.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> References: <20210109132921.140932-1-angelogioacchino.delregno@somainline.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org In commit 208921bae696 ("arm64: dts: qcom: pmi8998: Add nodes for LAB and IBB regulators") bindings for the lab/ibb regulators were added to the pmi8998 dt, but the original committer has never specified what the interrupts were for. LAB and IBB regulators provide two interrupts, SC-ERR (short circuit error) and VREG-OK but, in that commit, the regulators were provided with two different types of interrupts; specifically, IBB had the SC-ERR interrupt, while LAB had the VREG-OK one, none of which were (luckily) used, since the driver didn't actually use these at all. Assuming that the original intention was to have the SC IRQ in both LAB and IBB, as per the names appearing in documentation, fix the SCP interrupt. While at it, also add the OCP interrupt in order to be able to enable the Over-Current Protection feature, if requested. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index c8e21713cb9f..5742163d49b3 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -30,11 +30,15 @@ labibb { compatible = "qcom,pmi8998-lab-ibb"; ibb: ibb { - interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>; + interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>, + <0x3 0xdc 0x0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "sc-err", "ocp"; }; lab: lab { - interrupts = <0x3 0xde 0x0 IRQ_TYPE_EDGE_RISING>; + interrupts = <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>, + <0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "sc-err", "ocp"; }; };