From 756a59e3219bbf2df9a415dc4fcc040bb9e77bef Mon Sep 17 00:00:00 2001 From: Hui Liu Date: Thu, 11 Aug 2022 15:17:46 +0800 Subject: [PATCH 1/3] iio: adc: qcom-spmi-adc5-gen3: add hibernation support Free the ADC interrupt before entering into hibernation and request the interrupts again after restore from hibernation. Change-Id: I7f1d87a90da4d080e8ccefa066e3a210685a1375 Signed-off-by: Hui Liu --- drivers/iio/adc/qcom-spmi-adc5-gen3.c | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c index 5560625e462d..0a0992101085 100644 --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -1773,10 +1773,47 @@ static int adc5_gen3_exit(struct platform_device *pdev) return 0; } +static int adc5_gen3_freeze(struct device *dev) +{ + struct adc5_chip *adc = dev_get_drvdata(dev); + int i = 0; + + mutex_lock(&adc->lock); + + for (i = 0; i < adc->num_sdams; i++) + devm_free_irq(dev, adc->base[i].irq, adc); + + mutex_unlock(&adc->lock); + + return 0; +} + +static int adc5_gen3_restore(struct device *dev) +{ + struct adc5_chip *adc = dev_get_drvdata(dev); + int i = 0; + int ret = 0; + + for (i = 0; i < adc->num_sdams; i++) { + ret = devm_request_irq(dev, adc->base[i].irq, adc5_gen3_isr, + 0, adc->base[i].irq_name, adc); + if (ret < 0) + return ret; + } + + return ret; +} + +static const struct dev_pm_ops adc5_gen3_pm_ops = { + .freeze = adc5_gen3_freeze, + .restore = adc5_gen3_restore, +}; + static struct platform_driver adc5_gen3_driver = { .driver = { .name = "qcom-spmi-adc5-gen3", .of_match_table = adc5_match_table, + .pm = &adc5_gen3_pm_ops, }, .probe = adc5_gen3_probe, .remove = adc5_gen3_exit, From 27d05118d531863e7a33c95b704a7f440484ee86 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 7 Jul 2022 15:42:10 -0700 Subject: [PATCH 2/3] dt-bindings: regulator: rpmh-regulator: add new LOW_SVS and NOM levels Add constants for voltage levels: LOW_SVS_D0, LOW_SVS_D2, LOW_SVS_P1, and NOM_L0. Change-Id: I9b74387eda894a6323a688af1e3a3c021a444af3 Signed-off-by: David Collins --- include/dt-bindings/regulator/qcom,rpmh-regulator-levels.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dt-bindings/regulator/qcom,rpmh-regulator-levels.h b/include/dt-bindings/regulator/qcom,rpmh-regulator-levels.h index 1d6601b6023a..326fbfdad875 100644 --- a/include/dt-bindings/regulator/qcom,rpmh-regulator-levels.h +++ b/include/dt-bindings/regulator/qcom,rpmh-regulator-levels.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __QCOM_RPMH_REGULATOR_LEVELS_H @@ -9,8 +10,11 @@ /* These levels may be used for ARC type RPMh regulators. */ #define RPMH_REGULATOR_LEVEL_RETENTION 16 #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 +#define RPMH_REGULATOR_LEVEL_LOW_SVS_D2 52 #define RPMH_REGULATOR_LEVEL_LOW_SVS_D1 56 +#define RPMH_REGULATOR_LEVEL_LOW_SVS_D0 60 #define RPMH_REGULATOR_LEVEL_LOW_SVS 64 +#define RPMH_REGULATOR_LEVEL_LOW_SVS_P1 72 #define RPMH_REGULATOR_LEVEL_LOW_SVS_L1 80 #define RPMH_REGULATOR_LEVEL_LOW_SVS_L2 96 #define RPMH_REGULATOR_LEVEL_SVS 128 @@ -18,6 +22,7 @@ #define RPMH_REGULATOR_LEVEL_SVS_L1 192 #define RPMH_REGULATOR_LEVEL_SVS_L2 224 #define RPMH_REGULATOR_LEVEL_NOM 256 +#define RPMH_REGULATOR_LEVEL_NOM_L0 288 #define RPMH_REGULATOR_LEVEL_NOM_L1 320 #define RPMH_REGULATOR_LEVEL_NOM_L2 336 #define RPMH_REGULATOR_LEVEL_TURBO 384 From d71c7b2e126216c531462f56965ef87e83047c37 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Wed, 8 Jun 2022 13:51:35 -0700 Subject: [PATCH 3/3] pwm: pwm-qti-lpg: Add Support for high resolution PWM Currently, PMK8550 PMIC has a high resolution PWM module that is unused. This PWM module can support from 8-bit - 15-bit PWM. Implement PWM code so that PMK8550's PWM can be used. Change-Id: Ic0184528e8195eee38b482f0778e63fc63e0c2ed Signed-off-by: Anjelique Melendez --- drivers/pwm/pwm-qti-lpg.c | 128 ++++++++++++++++++++++++++++---------- 1 file changed, 95 insertions(+), 33 deletions(-) diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c index dd8a32536603..416e1a23f03f 100644 --- a/drivers/pwm/pwm-qti-lpg.c +++ b/drivers/pwm/pwm-qti-lpg.c @@ -62,14 +62,18 @@ /* REG_LPG_PERPH_SUBTYPE */ #define SUBTYPE_PWM 0x0b +#define SUBTYPE_HI_RES_PWM 0x0c #define SUBTYPE_LPG_LITE 0x11 /* REG_LPG_PWM_SIZE_CLK */ #define LPG_PWM_SIZE_LPG_MASK BIT(4) #define LPG_PWM_SIZE_PWM_MASK BIT(2) +#define LPG_PWM_SIZE_PWM_HI_RES_MASK GENMASK(6, 4) #define LPG_PWM_SIZE_LPG_SHIFT 4 #define LPG_PWM_SIZE_PWM_SHIFT 2 +#define LPG_PWM_SIZE_PWM_HI_RES_SHIFT 4 #define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0) +#define LPG_PWM_HI_RES_CLK_FREQ_SEL_MASK GENMASK(2, 0) /* REG_LPG_PWM_FREQ_PREDIV_CLK */ #define LPG_PWM_FREQ_PREDIV_MASK GENMASK(6, 5) @@ -84,6 +88,7 @@ /* REG_LPG_PWM_VALUE_MSB */ #define LPG_PWM_VALUE_MSB_MASK BIT(0) +#define LPG_PWM_HI_RES_VALUE_MSB_MASK GENMASK(7, 0) /* REG_LPG_ENABLE_CONTROL */ #define LPG_EN_LPG_OUT_BIT BIT(7) @@ -97,7 +102,9 @@ #define LPG_PWM_VALUE_SYNC BIT(0) #define NUM_PWM_SIZE 2 +#define NUM_PWM_HI_RES_SIZE 8 #define NUM_PWM_CLK 3 +#define NUM_PWM_HI_RES_CLK 4 #define NUM_CLK_PREDIV 4 #define NUM_PWM_EXP 8 @@ -147,9 +154,12 @@ enum ppg_num_nvmems { }; static const int pwm_size[NUM_PWM_SIZE] = {6, 9}; +static const int pwm_hi_res_size[NUM_PWM_HI_RES_SIZE] = {8, 9, 10, 11, 12, 13, 14, 15}; static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000}; +static const int clk_freq_hz_hi_res[NUM_PWM_HI_RES_CLK] = {1024, 32768, 19200000, 76800000}; /* clk_period_ns = NSEC_PER_SEC / clk_freq_hz */ static const int clk_period_ns[NUM_PWM_CLK] = {976562, 30517, 52}; +static const int clk_period_ns_hi_res[NUM_PWM_HI_RES_CLK] = {976562, 30517, 52, 13}; static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6}; static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7}; @@ -428,10 +438,18 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) u8 val, mask, shift; int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx; - pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size, - pwm_size, ARRAY_SIZE(pwm_size)); - pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, - clk_freq_hz, ARRAY_SIZE(clk_freq_hz)); + if (lpg->subtype == SUBTYPE_HI_RES_PWM) { + pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size, + pwm_hi_res_size, ARRAY_SIZE(pwm_hi_res_size)); + pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, + clk_freq_hz_hi_res, ARRAY_SIZE(clk_freq_hz_hi_res)); + } else { + pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size, + pwm_size, ARRAY_SIZE(pwm_size)); + pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, + clk_freq_hz, ARRAY_SIZE(clk_freq_hz)); + } + prediv_idx = __find_index_in_array(lpg->pwm_config.prediv, clk_prediv, ARRAY_SIZE(clk_prediv)); clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp, @@ -445,14 +463,16 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) pwm_clk_idx += 1; if (lpg->subtype == SUBTYPE_PWM) { shift = LPG_PWM_SIZE_PWM_SHIFT; - mask = LPG_PWM_SIZE_PWM_MASK; + mask = LPG_PWM_SIZE_PWM_MASK | LPG_PWM_CLK_FREQ_SEL_MASK; + } else if (lpg->subtype == SUBTYPE_HI_RES_PWM) { + shift = LPG_PWM_SIZE_PWM_HI_RES_SHIFT; + mask = LPG_PWM_SIZE_PWM_HI_RES_MASK | LPG_PWM_HI_RES_CLK_FREQ_SEL_MASK; } else { shift = LPG_PWM_SIZE_LPG_SHIFT; - mask = LPG_PWM_SIZE_LPG_MASK; + mask = LPG_PWM_SIZE_LPG_MASK | LPG_PWM_CLK_FREQ_SEL_MASK; } val = pwm_size_idx << shift | pwm_clk_idx; - mask |= LPG_PWM_CLK_FREQ_SEL_MASK; rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val); if (rc < 0) { dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n", @@ -473,7 +493,10 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) return 0; val = lpg->pwm_config.pwm_value >> 8; - mask = LPG_PWM_VALUE_MSB_MASK; + if (lpg->subtype == SUBTYPE_HI_RES_PWM) + mask = LPG_PWM_HI_RES_VALUE_MSB_MASK; + else + mask = LPG_PWM_VALUE_MSB_MASK; rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val); if (rc < 0) { dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n", @@ -506,8 +529,13 @@ static int qpnp_lpg_set_pfm_config(struct qpnp_lpg_channel *lpg) u8 val, mask; int pwm_clk_idx, clk_exp_idx; - pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, - clk_freq_hz, ARRAY_SIZE(clk_freq_hz)); + if (lpg->subtype == SUBTYPE_HI_RES_PWM) + pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, + clk_freq_hz_hi_res, ARRAY_SIZE(clk_freq_hz_hi_res)); + else + pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk, + clk_freq_hz, ARRAY_SIZE(clk_freq_hz)); + clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp, pwm_exponent, ARRAY_SIZE(pwm_exponent)); @@ -518,7 +546,11 @@ static int qpnp_lpg_set_pfm_config(struct qpnp_lpg_channel *lpg) pwm_clk_idx += 1; val = pwm_clk_idx; - mask = LPG_PWM_CLK_FREQ_SEL_MASK; + if (lpg->subtype == SUBTYPE_HI_RES_PWM) + mask = LPG_PWM_HI_RES_CLK_FREQ_SEL_MASK; + else + mask = LPG_PWM_CLK_FREQ_SEL_MASK; + rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val); if (rc < 0) { dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n", @@ -837,13 +869,14 @@ static int __qpnp_lpg_calc_pwm_period(u64 period_ns, { struct qpnp_lpg_channel *lpg = container_of(pwm_config, struct qpnp_lpg_channel, pwm_config); - struct lpg_pwm_config configs[NUM_PWM_SIZE]; + struct lpg_pwm_config configs[NUM_PWM_HI_RES_SIZE]; int i, j, m, n; u64 tmp1, tmp2; u64 clk_period_ns = 0, pwm_clk_period_ns; u64 clk_delta_ns = U64_MAX, min_clk_delta_ns = U64_MAX; u64 pwm_period_delta = U64_MAX, min_pwm_period_delta = U64_MAX; - int pwm_size_step; + int pwm_size_step, clk_len, pwm_size_len; + const int *pwm_size_arr, *clk_freq_hz_arr; /* * (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC @@ -852,20 +885,34 @@ static int __qpnp_lpg_calc_pwm_period(u64 period_ns, * * Searching the closest settings for the requested PWM period. */ - if (lpg->chip->use_sdam) + if (lpg->subtype == SUBTYPE_HI_RES_PWM) { + pwm_size_arr = pwm_hi_res_size; + pwm_size_len = NUM_PWM_HI_RES_SIZE; + clk_freq_hz_arr = clk_freq_hz_hi_res; + clk_len = NUM_PWM_HI_RES_CLK; + } else { + pwm_size_arr = pwm_size; + pwm_size_len = NUM_PWM_SIZE; + clk_freq_hz_arr = clk_freq_hz; + clk_len = NUM_PWM_CLK; + } + + if (lpg->chip->use_sdam) { /* SDAM pattern control can only use 9 bit resolution */ n = 1; + pwm_size_len = 2; + } else n = 0; - for (; n < ARRAY_SIZE(pwm_size); n++) { - pwm_clk_period_ns = period_ns >> pwm_size[n]; - for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) { - for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) { + for (; n < pwm_size_len; n++) { + pwm_clk_period_ns = period_ns >> pwm_size_arr[n]; + for (i = clk_len - 1; i >= 0; i--) { + for (j = 0; j < clk_len; j++) { for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) { tmp1 = 1 << pwm_exponent[m]; tmp1 *= clk_prediv[j]; tmp2 = NSEC_PER_SEC; - do_div(tmp2, clk_freq_hz[i]); + do_div(tmp2, clk_freq_hz_arr[i]); clk_period_ns = tmp1 * tmp2; @@ -879,13 +926,13 @@ static int __qpnp_lpg_calc_pwm_period(u64 period_ns, min_clk_delta_ns = clk_delta_ns; configs[n].pwm_clk - = clk_freq_hz[i]; + = clk_freq_hz_arr[i]; configs[n].prediv = clk_prediv[j]; configs[n].clk_exp = pwm_exponent[m]; configs[n].pwm_size - = pwm_size[n]; + = pwm_size_arr[n]; configs[n].best_period_ns = clk_period_ns; } @@ -893,9 +940,9 @@ static int __qpnp_lpg_calc_pwm_period(u64 period_ns, } } - configs[n].best_period_ns *= 1 << pwm_size[n]; + configs[n].best_period_ns *= 1 << pwm_size_arr[n]; /* Find the closest setting for PWM period */ - pwm_period_delta = min_clk_delta_ns << pwm_size[n]; + pwm_period_delta = min_clk_delta_ns << pwm_size_arr[n]; if (pwm_period_delta < min_pwm_period_delta) { min_pwm_period_delta = pwm_period_delta; memcpy(pwm_config, &configs[n], @@ -904,12 +951,12 @@ static int __qpnp_lpg_calc_pwm_period(u64 period_ns, } /* Larger PWM size can achieve better resolution for PWM duty */ - for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) { - if (pwm_config->pwm_size >= pwm_size[n]) + for (n = pwm_size_len - 1; n > 0; n--) { + if (pwm_config->pwm_size >= pwm_size_arr[n]) break; - pwm_size_step = pwm_size[n] - pwm_config->pwm_size; + pwm_size_step = pwm_size_arr[n] - pwm_config->pwm_size; if (pwm_config->clk_exp >= pwm_size_step) { - pwm_config->pwm_size = pwm_size[n]; + pwm_config->pwm_size = pwm_size_arr[n]; pwm_config->clk_exp -= pwm_size_step; } } @@ -939,10 +986,13 @@ static void __qpnp_lpg_calc_pwm_duty(u64 period_ns, u64 duty_ns, static int __qpnp_lpg_calc_pfm_period(u64 period_ns, struct lpg_pwm_config *pwm_config) { - int clk, exp, lsb; + struct qpnp_lpg_channel *lpg = container_of(pwm_config, + struct qpnp_lpg_channel, pwm_config); + int clk, exp, lsb, clk_len; int best_clk = 0, best_exp = 0, best_lsb = -EINVAL; u64 lsb_tmp, period_tmp, curr_p_err, last_p_err = 0; u64 min_p_err = U64_MAX; + const int *clk_freq_hz_arr, *clk_period_ns_arr; /* * 2 * (pwm_value_lsb + 1) * (2^pwm_exp) * NSEC_PER_SEC @@ -954,16 +1004,27 @@ static int __qpnp_lpg_calc_pfm_period(u64 period_ns, * period. Store the triplet values that yield the closest value to * desired period. */ - for (clk = 0; clk < NUM_PWM_CLK; clk++) { + + if (lpg->subtype == SUBTYPE_HI_RES_PWM) { + clk_freq_hz_arr = clk_freq_hz_hi_res; + clk_period_ns_arr = clk_period_ns_hi_res; + clk_len = NUM_PWM_HI_RES_CLK; + } else { + clk_freq_hz_arr = clk_freq_hz; + clk_period_ns_arr = clk_period_ns; + clk_len = NUM_PWM_CLK; + } + + for (clk = 0; clk < clk_len; clk++) { last_p_err = U64_MAX; for (exp = 0; exp < NUM_PWM_EXP; exp++) { - lsb_tmp = div_u64(period_ns, clk_period_ns[clk]); + lsb_tmp = div_u64(period_ns, clk_period_ns_arr[clk]); lsb_tmp >>= (exp + 1); lsb = lsb_tmp - 1; if (lsb >= 0 && lsb <= U8_MAX) { period_tmp = (lsb + 1); period_tmp <<= (exp + 1); - period_tmp *= clk_period_ns[clk]; + period_tmp *= clk_period_ns_arr[clk]; curr_p_err = abs(period_ns - period_tmp); if (curr_p_err < min_p_err) { @@ -994,7 +1055,7 @@ static int __qpnp_lpg_calc_pfm_period(u64 period_ns, return -EINVAL; } - pwm_config->pwm_clk = clk_freq_hz[best_clk]; + pwm_config->pwm_clk = clk_freq_hz_arr[best_clk]; pwm_config->clk_exp = pwm_exponent[best_exp]; pwm_config->pwm_value = best_lsb; @@ -1794,7 +1855,8 @@ static int qpnp_lpg_parse_pfm_support(struct qpnp_lpg_chip *chip) chan_idx--; /* zero-based indexing */ - if (chip->lpgs[chan_idx].subtype == SUBTYPE_PWM) { + if (chip->lpgs[chan_idx].subtype == SUBTYPE_PWM || + chip->lpgs[chan_idx].subtype == SUBTYPE_HI_RES_PWM) { rc = qpnp_lpg_read(&chip->lpgs[chan_idx], REG_PWM_STATUS1, &val); if (rc < 0) {