Merge "regulator: amoled: Add IBB spur mitigation support"

This commit is contained in:
qctecmdr 2023-10-11 01:16:31 -07:00 committed by Gerrit - the friendly Code Review server
commit 2ac315a014

View File

@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@ -17,7 +18,7 @@
#include <linux/regulator/machine.h>
/* Register definitions */
#define PERIPH_TYPE 0x04
#define PERIPH_REVISION4 0x03
#define IBB_PERIPH_TYPE 0x20
#define AB_PERIPH_TYPE 0x24
#define OLEDB_PERIPH_TYPE 0x2C
@ -44,6 +45,29 @@
#define FORCE_DUAL_PHASE_BIT BIT(1)
#define FORCE_SINGLE_PHASE_BIT BIT(0)
/* IBB SPUR FSM/SQM CTL */
#define IBB_SPUR_CTL(chip) (chip->ibb_base + 0xB6)
#define SPUR_FSM_EN BIT(7)
#define SPUR_SQM_EN BIT(6)
#define IBB_SPUR_FREQ_CTL(chip) (chip->ibb_base + 0xB7)
#define FREQ_RES_SEL BIT(0)
#define IBB_SPUR_FREQ_THRESH_HIGH(i) (chip->ibb_base + 0xB8 + i*2)
#define IBB_SPUR_FREQ_THRESH_LOW(i) (chip->ibb_base + 0xB9 + i*2)
#define MAX_SPUR_FREQ_BANDS 3
#define MAX_SPUR_FREQ_KHZ 248
#define AMOLED_SDAM_OFFSET 0xB8
#define SQM_TIMER_LOWER_LIMIT_MS 100
#define SQM_TIMER_UPPER_LIMIT_MS 10000
enum {
SPUR_MITIGATION_DISABLED,
SPUR_MITIGATION_ENABLED_WITHOUT_SQM,
SPUR_MITIGATION_ENABLED_WITH_SQM,
};
struct amoled_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
@ -70,11 +94,19 @@ struct ab_regulator {
struct ibb_regulator {
struct amoled_regulator vreg;
u8 subtype;
u8 rev4;
/* DT params */
bool swire_control;
bool pd_control;
bool single_phase;
/* ibb_spur_mitigation params */
u32 spur_mitigation_level;
u32 spur_sqm_timer_ms;
u32 spur_freq_thresh_high[MAX_SPUR_FREQ_BANDS];
u32 spur_freq_thresh_low[MAX_SPUR_FREQ_BANDS];
bool spur_freq_res_sel;
};
struct qpnp_amoled {
@ -83,7 +115,7 @@ struct qpnp_amoled {
struct oledb_regulator oledb;
struct ab_regulator ab;
struct ibb_regulator ibb;
struct nvmem_cell *nvmem_cell;
/* DT params */
u32 oledb_base;
u32 ab_base;
@ -101,6 +133,19 @@ enum ibb_subtype {
PM8350B_IBB = 0x04,
};
enum ibb_rev4 {
IBB_ANA_MAJOR_V1 = 0x01,
IBB_ANA_MAJOR_V2 = 0x02,
};
static inline bool is_spur_mitigation_supported(struct ibb_regulator *ibb)
{
if ((ibb->subtype == PM8350B_IBB) && (ibb->rev4 >= IBB_ANA_MAJOR_V2))
return true;
return false;
}
static inline bool is_phase_ctrl_supported(struct ibb_regulator *ibb)
{
if (ibb->subtype == PM8350B_IBB)
@ -540,13 +585,76 @@ static int qpnp_amoled_hw_init(struct qpnp_amoled *chip)
return 0;
}
static int qpnp_amoled_ibb_spur_parse_dt(struct qpnp_amoled *chip, struct device_node *node)
{
int freq_array_len, rc, i;
u32 spur_thres[2*MAX_SPUR_FREQ_BANDS];
rc = of_property_read_u32(node,
"qcom,ibb-spur-mitigation-level",
&chip->ibb.spur_mitigation_level);
if (rc < 0 || (chip->ibb.spur_mitigation_level == SPUR_MITIGATION_DISABLED)) {
dev_dbg(chip->dev, "ibb spur mitigation DISABLED!");
return rc;
}
if (chip->ibb.spur_mitigation_level == SPUR_MITIGATION_ENABLED_WITH_SQM) {
of_property_read_u32(node, "qcom,ibb-spur-sqm-timer-ms",
&chip->ibb.spur_sqm_timer_ms);
chip->nvmem_cell = devm_nvmem_cell_get(chip->dev,
"ibb_spur_sqm_timer");
if (IS_ERR(chip->nvmem_cell)) {
rc = PTR_ERR(chip->nvmem_cell);
if (rc != -EPROBE_DEFER)
dev_err(chip->dev, "Failed to get nvmem-cells, rc=%d\n", rc);
return rc;
}
}
/*
* Read the step size - 1khz or 2khz.
*
* NOTE: Even if this is not defined, step size may still be
* set to 2khz indirectly, if any freq1/2/3 thresh limit
* is in range: 248khz < f < 496khz.
*/
chip->ibb.spur_freq_res_sel = of_property_read_bool(node,
"qcom,ibb-spur-2khz-step-size");
freq_array_len = of_property_count_elems_of_size(node,
"qcom,ibb-spur-freq-thresholds", sizeof(u32));
if (freq_array_len != 2*MAX_SPUR_FREQ_BANDS) {
dev_err(chip->dev, "invalid ibb spur freq threshold array size = %d\n",
freq_array_len);
chip->ibb.spur_mitigation_level = SPUR_MITIGATION_DISABLED;
return -EINVAL;
}
rc = of_property_read_u32_array(node,
"qcom,ibb-spur-freq-thresholds", spur_thres, freq_array_len);
if (rc < 0) {
dev_err(chip->dev, "failed to read thresholds = %d\n", rc);
return rc;
}
for (i = 0; i < MAX_SPUR_FREQ_BANDS; i++) {
chip->ibb.spur_freq_thresh_low[i] = spur_thres[2*i];
chip->ibb.spur_freq_thresh_high[i] = spur_thres[(2*i)+1];
}
return rc;
}
static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
{
struct device_node *temp, *node = chip->dev->of_node;
const __be32 *prop_addr;
int rc = 0;
u32 base;
u8 val[2];
u8 val[3];
for_each_available_child_of_node(node, temp) {
prop_addr = of_get_address(temp, 0, NULL, NULL);
@ -556,13 +664,13 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
}
base = be32_to_cpu(*prop_addr);
rc = qpnp_amoled_read(chip, base + PERIPH_TYPE, val, 2);
rc = qpnp_amoled_read(chip, base + PERIPH_REVISION4, val, 3);
if (rc < 0) {
pr_err("Couldn't read PERIPH_TYPE for base %x\n", base);
pr_err("Couldn't read PERIPH_REVISION4 for base %x\n", base);
return rc;
}
switch (val[0]) {
switch (val[1]) {
case OLEDB_PERIPH_TYPE:
chip->oledb_base = base;
chip->oledb.vreg.node = temp;
@ -579,7 +687,9 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
break;
case IBB_PERIPH_TYPE:
chip->ibb_base = base;
chip->ibb.subtype = val[1];
chip->ibb.subtype = val[2];
chip->ibb.rev4 = val[0];
chip->ibb.vreg.node = temp;
chip->ibb.swire_control = of_property_read_bool(temp,
"qcom,swire-control");
@ -587,6 +697,13 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
"qcom,aod-pd-control");
chip->ibb.single_phase = of_property_read_bool(temp,
"qcom,ibb-single-phase");
if (is_spur_mitigation_supported(&chip->ibb)) {
rc = qpnp_amoled_ibb_spur_parse_dt(chip, temp);
if (rc < 0)
pr_err("Failed to parse ibb_spur_parse_dt\n");
}
break;
default:
pr_err("Unknown peripheral type 0x%x\n", val[0]);
@ -597,6 +714,167 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
return 0;
}
static bool is_2khz_step_needed(struct qpnp_amoled *chip)
{
u8 i;
/*
* If any of the freq1/2/3 band has valid thresh
* (i.e f_high >= f_low)
* and freq values is in range of 248khz < f < 496khz
* then use step_size = 2khz
*/
for (i = 0; i < MAX_SPUR_FREQ_BANDS; i++) {
if ((chip->ibb.spur_freq_thresh_high[i] > MAX_SPUR_FREQ_KHZ) &&
(chip->ibb.spur_freq_thresh_high[i] < MAX_SPUR_FREQ_KHZ * 2) &&
(chip->ibb.spur_freq_thresh_high[i] >=
chip->ibb.spur_freq_thresh_low[i])) {
return true;
}
}
return false;
}
static int qpnp_amoled_ibb_spur_set_thresh(struct qpnp_amoled *chip)
{
int i = 0, rc = 0;
u16 low, high, max, temp = 0;
if (!chip->ibb.spur_freq_res_sel)
chip->ibb.spur_freq_res_sel = is_2khz_step_needed(chip);
rc = qpnp_amoled_masked_write(chip,
IBB_SPUR_FREQ_CTL(chip),
FREQ_RES_SEL,
(chip->ibb.spur_freq_res_sel ? FREQ_RES_SEL : 0));
if (rc < 0) {
dev_err(chip->dev, "failed to write IBB_SPUR_CTL register!\n");
return rc;
}
/* Calculate max based on the step size */
max = MAX_SPUR_FREQ_KHZ * (chip->ibb.spur_freq_res_sel ? 2 : 1);
for (i = 0; i < MAX_SPUR_FREQ_BANDS; i++) {
low = chip->ibb.spur_freq_thresh_low[i];
high = chip->ibb.spur_freq_thresh_high[i];
if (high < low || low > max || high > max) {
dev_err(chip->dev, "ibb spur freq band%d threshold invalid!\n",
(i+1));
/* Set both thresholds to max to in effect disable it */
chip->ibb.spur_freq_thresh_high[i] = max;
chip->ibb.spur_freq_thresh_low[i] = max;
low = max;
high = max;
}
/*
*For High threshold, roundoff-to-ceiling for odd frequency
* with 2khz step
*/
temp = high / (chip->ibb.spur_freq_res_sel ? 2 : 1);
temp += chip->ibb.spur_freq_res_sel ? (high % 2) : 0;
rc = qpnp_amoled_write(chip, IBB_SPUR_FREQ_THRESH_HIGH(i),
(u8 *)&temp, 1);
if (rc < 0) {
dev_err(chip->dev, "failed to write IBB_SPUR_FREQ_HIGH register!\n");
return rc;
}
/*
* For Low threshold, roundoff-to-floor for odd frequency
* with 2khz step
*/
temp = low / (chip->ibb.spur_freq_res_sel ? 2 : 1);
rc = qpnp_amoled_write(chip, IBB_SPUR_FREQ_THRESH_LOW(i),
(u8 *)&temp, 1);
if (rc < 0) {
dev_err(chip->dev, "failed to write IBB_SPUR_FREQ_LOW register!\n");
return rc;
}
}
return 0;
}
static int qpnp_amoled_ibb_spur_set_sqm_timer(struct qpnp_amoled *chip, u16 sqm_timer)
{
return nvmem_cell_write(chip->nvmem_cell,
&sqm_timer,
sizeof(sqm_timer));
}
static int qpnp_amoled_ibb_spur_init(struct qpnp_amoled *chip)
{
int rc = 0;
switch (chip->ibb.spur_mitigation_level) {
case SPUR_MITIGATION_ENABLED_WITH_SQM:
/*set SQM mode */
rc = qpnp_amoled_masked_write(chip, IBB_SPUR_CTL(chip),
SPUR_SQM_EN,
SPUR_SQM_EN);
if (rc < 0) {
dev_err(chip->dev, "failed to enable spur SQM mode!\n");
return rc;
}
/*set SQM timer if defined */
if (chip->ibb.spur_sqm_timer_ms > SQM_TIMER_LOWER_LIMIT_MS &&
chip->ibb.spur_sqm_timer_ms < SQM_TIMER_UPPER_LIMIT_MS) {
rc = qpnp_amoled_ibb_spur_set_sqm_timer(chip,
(u16)chip->ibb.spur_sqm_timer_ms);
if (rc < 0) {
if (rc != -EPROBE_DEFER)
dev_err(chip->dev,
"failed to enable spur SQM timer\n");
return rc;
}
}
fallthrough;
case SPUR_MITIGATION_ENABLED_WITHOUT_SQM:
rc = qpnp_amoled_ibb_spur_set_thresh(chip);
if (rc < 0) {
dev_err(chip->dev, "failed to set spur thresholds!\n");
return rc;
}
rc = qpnp_amoled_masked_write(chip, IBB_SPUR_CTL(chip),
SPUR_FSM_EN,
SPUR_FSM_EN);
if (rc < 0) {
dev_err(chip->dev, "failed to enable spur FSM!\n");
return rc;
}
break;
case SPUR_MITIGATION_DISABLED:
default:
/* disable ibb spur FSM */
rc = qpnp_amoled_masked_write(chip, IBB_SPUR_CTL(chip),
SPUR_FSM_EN,
0);
if (rc < 0) {
dev_err(chip->dev, "failed to disable spur FSM!\n");
return rc;
}
}
return 0;
}
static int qpnp_amoled_regulator_probe(struct platform_device *pdev)
{
int rc;
@ -634,6 +912,13 @@ static int qpnp_amoled_regulator_probe(struct platform_device *pdev)
if (rc < 0)
dev_err(chip->dev, "Failed to initialize HW rc=%d\n", rc);
if (is_spur_mitigation_supported(&chip->ibb)) {
rc = qpnp_amoled_ibb_spur_init(chip);
if (rc < 0)
dev_err(chip->dev, "Failed to init ibb spur settings rc=%d\n",
rc);
}
error:
return rc;
}