Merge "regulator: amoled: Add IBB spur mitigation support"
This commit is contained in:
commit
2ac315a014
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user