summaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2020-10-06 18:05:13 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2020-10-22 18:38:21 +0200
commita7305e684fcfb33029fe3d0af6b7d8dc4c8ca7a1 (patch)
treee80f85ed5b5b579dc7aca3b36398c238aba05f5a /drivers/power
parentbca815d620544c27288abf4841e39922d694425c (diff)
PM: AVS: qcom-cpr: Move the driver to the qcom specific drivers
The avs drivers are all SoC specific drivers that doesn't share any code. Instead they are located in a directory, mostly to keep similar functionality together. From a maintenance point of view, it makes better sense to collect SoC specific drivers like these, into the SoC specific directories. Therefore, let's move the qcom-cpr driver to the qcom directory. Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Acked-by: Niklas Cassel <nks@flawful.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/avs/Kconfig16
-rw-r--r--drivers/power/avs/Makefile1
-rw-r--r--drivers/power/avs/qcom-cpr.c1788
3 files changed, 0 insertions, 1805 deletions
diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig
index d789509ae7e9..a4e40e534e6a 100644
--- a/drivers/power/avs/Kconfig
+++ b/drivers/power/avs/Kconfig
@@ -1,17 +1 @@
# SPDX-License-Identifier: GPL-2.0-only
-
-config QCOM_CPR
- tristate "QCOM Core Power Reduction (CPR) support"
- depends on POWER_AVS && HAS_IOMEM
- select PM_OPP
- select REGMAP
- help
- Say Y here to enable support for the CPR hardware found on Qualcomm
- SoCs like QCS404.
-
- This driver populates CPU OPPs tables and makes adjustments to the
- tables based on feedback from the CPR hardware. If you want to do
- CPUfrequency scaling say Y here.
-
- To compile this driver as a module, choose M here: the module will
- be called qcom-cpr
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 735832f47214..a4e40e534e6a 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -1,2 +1 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_QCOM_CPR) += qcom-cpr.o
diff --git a/drivers/power/avs/qcom-cpr.c b/drivers/power/avs/qcom-cpr.c
deleted file mode 100644
index b24cc77d1889..000000000000
--- a/drivers/power/avs/qcom-cpr.c
+++ /dev/null
@@ -1,1788 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (c) 2019, Linaro Limited
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_opp.h>
-#include <linux/interrupt.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regulator/consumer.h>
-#include <linux/clk.h>
-#include <linux/nvmem-consumer.h>
-
-/* Register Offsets for RB-CPR and Bit Definitions */
-
-/* RBCPR Version Register */
-#define REG_RBCPR_VERSION 0
-#define RBCPR_VER_2 0x02
-#define FLAGS_IGNORE_1ST_IRQ_STATUS BIT(0)
-
-/* RBCPR Gate Count and Target Registers */
-#define REG_RBCPR_GCNT_TARGET(n) (0x60 + 4 * (n))
-
-#define RBCPR_GCNT_TARGET_TARGET_SHIFT 0
-#define RBCPR_GCNT_TARGET_TARGET_MASK GENMASK(11, 0)
-#define RBCPR_GCNT_TARGET_GCNT_SHIFT 12
-#define RBCPR_GCNT_TARGET_GCNT_MASK GENMASK(9, 0)
-
-/* RBCPR Timer Control */
-#define REG_RBCPR_TIMER_INTERVAL 0x44
-#define REG_RBIF_TIMER_ADJUST 0x4c
-
-#define RBIF_TIMER_ADJ_CONS_UP_MASK GENMASK(3, 0)
-#define RBIF_TIMER_ADJ_CONS_UP_SHIFT 0
-#define RBIF_TIMER_ADJ_CONS_DOWN_MASK GENMASK(3, 0)
-#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT 4
-#define RBIF_TIMER_ADJ_CLAMP_INT_MASK GENMASK(7, 0)
-#define RBIF_TIMER_ADJ_CLAMP_INT_SHIFT 8
-
-/* RBCPR Config Register */
-#define REG_RBIF_LIMIT 0x48
-#define RBIF_LIMIT_CEILING_MASK GENMASK(5, 0)
-#define RBIF_LIMIT_CEILING_SHIFT 6
-#define RBIF_LIMIT_FLOOR_BITS 6
-#define RBIF_LIMIT_FLOOR_MASK GENMASK(5, 0)
-
-#define RBIF_LIMIT_CEILING_DEFAULT RBIF_LIMIT_CEILING_MASK
-#define RBIF_LIMIT_FLOOR_DEFAULT 0
-
-#define REG_RBIF_SW_VLEVEL 0x94
-#define RBIF_SW_VLEVEL_DEFAULT 0x20
-
-#define REG_RBCPR_STEP_QUOT 0x80
-#define RBCPR_STEP_QUOT_STEPQUOT_MASK GENMASK(7, 0)
-#define RBCPR_STEP_QUOT_IDLE_CLK_MASK GENMASK(3, 0)
-#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT 8
-
-/* RBCPR Control Register */
-#define REG_RBCPR_CTL 0x90
-
-#define RBCPR_CTL_LOOP_EN BIT(0)
-#define RBCPR_CTL_TIMER_EN BIT(3)
-#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN BIT(5)
-#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN BIT(6)
-#define RBCPR_CTL_COUNT_MODE BIT(10)
-#define RBCPR_CTL_UP_THRESHOLD_MASK GENMASK(3, 0)
-#define RBCPR_CTL_UP_THRESHOLD_SHIFT 24
-#define RBCPR_CTL_DN_THRESHOLD_MASK GENMASK(3, 0)
-#define RBCPR_CTL_DN_THRESHOLD_SHIFT 28
-
-/* RBCPR Ack/Nack Response */
-#define REG_RBIF_CONT_ACK_CMD 0x98
-#define REG_RBIF_CONT_NACK_CMD 0x9c
-
-/* RBCPR Result status Register */
-#define REG_RBCPR_RESULT_0 0xa0
-
-#define RBCPR_RESULT0_BUSY_SHIFT 19
-#define RBCPR_RESULT0_BUSY_MASK BIT(RBCPR_RESULT0_BUSY_SHIFT)
-#define RBCPR_RESULT0_ERROR_LT0_SHIFT 18
-#define RBCPR_RESULT0_ERROR_SHIFT 6
-#define RBCPR_RESULT0_ERROR_MASK GENMASK(11, 0)
-#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2
-#define RBCPR_RESULT0_ERROR_STEPS_MASK GENMASK(3, 0)
-#define RBCPR_RESULT0_STEP_UP_SHIFT 1
-
-/* RBCPR Interrupt Control Register */
-#define REG_RBIF_IRQ_EN(n) (0x100 + 4 * (n))
-#define REG_RBIF_IRQ_CLEAR 0x110
-#define REG_RBIF_IRQ_STATUS 0x114
-
-#define CPR_INT_DONE BIT(0)
-#define CPR_INT_MIN BIT(1)
-#define CPR_INT_DOWN BIT(2)
-#define CPR_INT_MID BIT(3)
-#define CPR_INT_UP BIT(4)
-#define CPR_INT_MAX BIT(5)
-#define CPR_INT_CLAMP BIT(6)
-#define CPR_INT_ALL (CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \
- CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP)
-#define CPR_INT_DEFAULT (CPR_INT_UP | CPR_INT_DOWN)
-
-#define CPR_NUM_RING_OSC 8
-
-/* CPR eFuse parameters */
-#define CPR_FUSE_TARGET_QUOT_BITS_MASK GENMASK(11, 0)
-
-#define CPR_FUSE_MIN_QUOT_DIFF 50
-
-#define FUSE_REVISION_UNKNOWN (-1)
-
-enum voltage_change_dir {
- NO_CHANGE,
- DOWN,
- UP,
-};
-
-struct cpr_fuse {
- char *ring_osc;
- char *init_voltage;
- char *quotient;
- char *quotient_offset;
-};
-
-struct fuse_corner_data {
- int ref_uV;
- int max_uV;
- int min_uV;
- int max_volt_scale;
- int max_quot_scale;
- /* fuse quot */
- int quot_offset;
- int quot_scale;
- int quot_adjust;
- /* fuse quot_offset */
- int quot_offset_scale;
- int quot_offset_adjust;
-};
-
-struct cpr_fuses {
- int init_voltage_step;
- int init_voltage_width;
- struct fuse_corner_data *fuse_corner_data;
-};
-
-struct corner_data {
- unsigned int fuse_corner;
- unsigned long freq;
-};
-
-struct cpr_desc {
- unsigned int num_fuse_corners;
- int min_diff_quot;
- int *step_quot;
-
- unsigned int timer_delay_us;
- unsigned int timer_cons_up;
- unsigned int timer_cons_down;
- unsigned int up_threshold;
- unsigned int down_threshold;
- unsigned int idle_clocks;
- unsigned int gcnt_us;
- unsigned int vdd_apc_step_up_limit;
- unsigned int vdd_apc_step_down_limit;
- unsigned int clamp_timer_interval;
-
- struct cpr_fuses cpr_fuses;
- bool reduce_to_fuse_uV;
- bool reduce_to_corner_uV;
-};
-
-struct acc_desc {
- unsigned int enable_reg;
- u32 enable_mask;
-
- struct reg_sequence *config;
- struct reg_sequence *settings;
- int num_regs_per_fuse;
-};
-
-struct cpr_acc_desc {
- const struct cpr_desc *cpr_desc;
- const struct acc_desc *acc_desc;
-};
-
-struct fuse_corner {
- int min_uV;
- int max_uV;
- int uV;
- int quot;
- int step_quot;
- const struct reg_sequence *accs;
- int num_accs;
- unsigned long max_freq;
- u8 ring_osc_idx;
-};
-
-struct corner {
- int min_uV;
- int max_uV;
- int uV;
- int last_uV;
- int quot_adjust;
- u32 save_ctl;
- u32 save_irq;
- unsigned long freq;
- struct fuse_corner *fuse_corner;
-};
-
-struct cpr_drv {
- unsigned int num_corners;
- unsigned int ref_clk_khz;
-
- struct generic_pm_domain pd;
- struct device *dev;
- struct device *attached_cpu_dev;
- struct mutex lock;
- void __iomem *base;
- struct corner *corner;
- struct regulator *vdd_apc;
- struct clk *cpu_clk;
- struct regmap *tcsr;
- bool loop_disabled;
- u32 gcnt;
- unsigned long flags;
-
- struct fuse_corner *fuse_corners;
- struct corner *corners;
-
- const struct cpr_desc *desc;
- const struct acc_desc *acc_desc;
- const struct cpr_fuse *cpr_fuses;
-
- struct dentry *debugfs;
-};
-
-static bool cpr_is_allowed(struct cpr_drv *drv)
-{
- return !drv->loop_disabled;
-}
-
-static void cpr_write(struct cpr_drv *drv, u32 offset, u32 value)
-{
- writel_relaxed(value, drv->base + offset);
-}
-
-static u32 cpr_read(struct cpr_drv *drv, u32 offset)
-{
- return readl_relaxed(drv->base + offset);
-}
-
-static void
-cpr_masked_write(struct cpr_drv *drv, u32 offset, u32 mask, u32 value)
-{
- u32 val;
-
- val = readl_relaxed(drv->base + offset);
- val &= ~mask;
- val |= value & mask;
- writel_relaxed(val, drv->base + offset);
-}
-
-static void cpr_irq_clr(struct cpr_drv *drv)
-{
- cpr_write(drv, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL);
-}
-
-static void cpr_irq_clr_nack(struct cpr_drv *drv)
-{
- cpr_irq_clr(drv);
- cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1);
-}
-
-static void cpr_irq_clr_ack(struct cpr_drv *drv)
-{
- cpr_irq_clr(drv);
- cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1);
-}
-
-static void cpr_irq_set(struct cpr_drv *drv, u32 int_bits)
-{
- cpr_write(drv, REG_RBIF_IRQ_EN(0), int_bits);
-}
-
-static void cpr_ctl_modify(struct cpr_drv *drv, u32 mask, u32 value)
-{
- cpr_masked_write(drv, REG_RBCPR_CTL, mask, value);
-}
-
-static void cpr_ctl_enable(struct cpr_drv *drv, struct corner *corner)
-{
- u32 val, mask;
- const struct cpr_desc *desc = drv->desc;
-
- /* Program Consecutive Up & Down */
- val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT;
- val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT;
- mask = RBIF_TIMER_ADJ_CONS_UP_MASK | RBIF_TIMER_ADJ_CONS_DOWN_MASK;
- cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, mask, val);
- cpr_masked_write(drv, REG_RBCPR_CTL,
- RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN |
- RBCPR_CTL_SW_AUTO_CONT_ACK_EN,
- corner->save_ctl);
- cpr_irq_set(drv, corner->save_irq);
-
- if (cpr_is_allowed(drv) && corner->max_uV > corner->min_uV)
- val = RBCPR_CTL_LOOP_EN;
- else
- val = 0;
- cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, val);
-}
-
-static void cpr_ctl_disable(struct cpr_drv *drv)
-{
- cpr_irq_set(drv, 0);
- cpr_ctl_modify(drv, RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN |
- RBCPR_CTL_SW_AUTO_CONT_ACK_EN, 0);
- cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST,
- RBIF_TIMER_ADJ_CONS_UP_MASK |
- RBIF_TIMER_ADJ_CONS_DOWN_MASK, 0);
- cpr_irq_clr(drv);
- cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1);
- cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1);
- cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, 0);
-}
-
-static bool cpr_ctl_is_enabled(struct cpr_drv *drv)
-{
- u32 reg_val;
-
- reg_val = cpr_read(drv, REG_RBCPR_CTL);
- return reg_val & RBCPR_CTL_LOOP_EN;
-}
-
-static bool cpr_ctl_is_busy(struct cpr_drv *drv)
-{
- u32 reg_val;
-
- reg_val = cpr_read(drv, REG_RBCPR_RESULT_0);
- return reg_val & RBCPR_RESULT0_BUSY_MASK;
-}
-
-static void cpr_corner_save(struct cpr_drv *drv, struct corner *corner)
-{
- corner->save_ctl = cpr_read(drv, REG_RBCPR_CTL);
- corner->save_irq = cpr_read(drv, REG_RBIF_IRQ_EN(0));
-}
-
-static void cpr_corner_restore(struct cpr_drv *drv, struct corner *corner)
-{
- u32 gcnt, ctl, irq, ro_sel, step_quot;
- struct fuse_corner *fuse = corner->fuse_corner;
- const struct cpr_desc *desc = drv->desc;
- int i;
-
- ro_sel = fuse->ring_osc_idx;
- gcnt = drv->gcnt;
- gcnt |= fuse->quot - corner->quot_adjust;
-
- /* Program the step quotient and idle clocks */
- step_quot = desc->idle_clocks << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT;
- step_quot |= fuse->step_quot & RBCPR_STEP_QUOT_STEPQUOT_MASK;
- cpr_write(drv, REG_RBCPR_STEP_QUOT, step_quot);
-
- /* Clear the target quotient value and gate count of all ROs */
- for (i = 0; i < CPR_NUM_RING_OSC; i++)
- cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0);
-
- cpr_write(drv, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt);
- ctl = corner->save_ctl;
- cpr_write(drv, REG_RBCPR_CTL, ctl);
- irq = corner->save_irq;
- cpr_irq_set(drv, irq);
- dev_dbg(drv->dev, "gcnt = %#08x, ctl = %#08x, irq = %#08x\n", gcnt,
- ctl, irq);
-}
-
-static void cpr_set_acc(struct regmap *tcsr, struct fuse_corner *f,
- struct fuse_corner *end)
-{
- if (f == end)
- return;
-
- if (f < end) {
- for (f += 1; f <= end; f++)
- regmap_multi_reg_write(tcsr, f->accs, f->num_accs);
- } else {
- for (f -= 1; f >= end; f--)
- regmap_multi_reg_write(tcsr, f->accs, f->num_accs);
- }
-}
-
-static int cpr_pre_voltage(struct cpr_drv *drv,
- struct fuse_corner *fuse_corner,
- enum voltage_change_dir dir)
-{
- struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner;
-
- if (drv->tcsr && dir == DOWN)
- cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner);
-
- return 0;
-}
-
-static int cpr_post_voltage(struct cpr_drv *drv,
- struct fuse_corner *fuse_corner,
- enum voltage_change_dir dir)
-{
- struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner;
-
- if (drv->tcsr && dir == UP)
- cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner);
-
- return 0;
-}
-
-static int cpr_scale_voltage(struct cpr_drv *drv, struct corner *corner,
- int new_uV, enum voltage_change_dir dir)
-{
- int ret;
- struct fuse_corner *fuse_corner = corner->fuse_corner;
-
- ret = cpr_pre_voltage(drv, fuse_corner, dir);
- if (ret)
- return ret;
-
- ret = regulator_set_voltage(drv->vdd_apc, new_uV, new_uV);
- if (ret) {
- dev_err_ratelimited(drv->dev, "failed to set apc voltage %d\n",
- new_uV);
- return ret;
- }
-
- ret = cpr_post_voltage(drv, fuse_corner, dir);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static unsigned int cpr_get_cur_perf_state(struct cpr_drv *drv)
-{
- return drv->corner ? drv->corner - drv->corners + 1 : 0;
-}
-
-static int cpr_scale(struct cpr_drv *drv, enum voltage_change_dir dir)
-{
- u32 val, error_steps, reg_mask;
- int last_uV, new_uV, step_uV, ret;
- struct corner *corner;
- const struct cpr_desc *desc = drv->desc;
-
- if (dir != UP && dir != DOWN)
- return 0;
-
- step_uV = regulator_get_linear_step(drv->vdd_apc);
- if (!step_uV)
- return -EINVAL;
-
- corner = drv->corner;
-
- val = cpr_read(drv, REG_RBCPR_RESULT_0);
-
- error_steps = val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT;
- error_steps &= RBCPR_RESULT0_ERROR_STEPS_MASK;
- last_uV = corner->last_uV;
-
- if (dir == UP) {
- if (desc->clamp_timer_interval &&
- error_steps < desc->up_threshold) {
- /*
- * Handle the case where another measurement started
- * after the interrupt was triggered due to a core
- * exiting from power collapse.
- */
- error_steps = max(desc->up_threshold,
- desc->vdd_apc_step_up_limit);
- }
-
- if (last_uV >= corner->max_uV) {
- cpr_irq_clr_nack(drv);
-
- /* Maximize the UP threshold */
- reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
- reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
- val = reg_mask;
- cpr_ctl_modify(drv, reg_mask, val);
-
- /* Disable UP interrupt */
- cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_UP);
-
- return 0;
- }
-
- if (error_steps > desc->vdd_apc_step_up_limit)
- error_steps = desc->vdd_apc_step_up_limit;
-
- /* Calculate new voltage */
- new_uV = last_uV + error_steps * step_uV;
- new_uV = min(new_uV, corner->max_uV);
-
- dev_dbg(drv->dev,
- "UP: -> new_uV: %d last_uV: %d perf state: %u\n",
- new_uV, last_uV, cpr_get_cur_perf_state(drv));
- } else {
- if (desc->clamp_timer_interval &&
- error_steps < desc->down_threshold) {
- /*
- * Handle the case where another measurement started
- * after the interrupt was triggered due to a core
- * exiting from power collapse.
- */
- error_steps = max(desc->down_threshold,
- desc->vdd_apc_step_down_limit);
- }
-
- if (last_uV <= corner->min_uV) {
- cpr_irq_clr_nack(drv);
-
- /* Enable auto nack down */
- reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
- val = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
-
- cpr_ctl_modify(drv, reg_mask, val);
-
- /* Disable DOWN interrupt */
- cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_DOWN);
-
- return 0;
- }
-
- if (error_steps > desc->vdd_apc_step_down_limit)
- error_steps = desc->vdd_apc_step_down_limit;
-
- /* Calculate new voltage */
- new_uV = last_uV - error_steps * step_uV;
- new_uV = max(new_uV, corner->min_uV);
-
- dev_dbg(drv->dev,
- "DOWN: -> new_uV: %d last_uV: %d perf state: %u\n",
- new_uV, last_uV, cpr_get_cur_perf_state(drv));
- }
-
- ret = cpr_scale_voltage(drv, corner, new_uV, dir);
- if (ret) {
- cpr_irq_clr_nack(drv);
- return ret;
- }
- drv->corner->last_uV = new_uV;
-
- if (dir == UP) {
- /* Disable auto nack down */
- reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
- val = 0;
- } else {
- /* Restore default threshold for UP */
- reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
- reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
- val = desc->up_threshold;
- val <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
- }
-
- cpr_ctl_modify(drv, reg_mask, val);
-
- /* Re-enable default interrupts */
- cpr_irq_set(drv, CPR_INT_DEFAULT);
-
- /* Ack */
- cpr_irq_clr_ack(drv);
-
- return 0;
-}
-
-static irqreturn_t cpr_irq_handler(int irq, void *dev)
-{
- struct cpr_drv *drv = dev;
- const struct cpr_desc *desc = drv->desc;
- irqreturn_t ret = IRQ_HANDLED;
- u32 val;
-
- mutex_lock(&drv->lock);
-
- val = cpr_read(drv, REG_RBIF_IRQ_STATUS);
- if (drv->flags & FLAGS_IGNORE_1ST_IRQ_STATUS)
- val = cpr_read(drv, REG_RBIF_IRQ_STATUS);
-
- dev_dbg(drv->dev, "IRQ_STATUS = %#02x\n", val);
-
- if (!cpr_ctl_is_enabled(drv)) {
- dev_dbg(drv->dev, "CPR is disabled\n");
- ret = IRQ_NONE;
- } else if (cpr_ctl_is_busy(drv) && !desc->clamp_timer_interval) {
- dev_dbg(drv->dev, "CPR measurement is not ready\n");
- } else if (!cpr_is_allowed(drv)) {
- val = cpr_read(drv, REG_RBCPR_CTL);
- dev_err_ratelimited(drv->dev,
- "Interrupt broken? RBCPR_CTL = %#02x\n",
- val);
- ret = IRQ_NONE;
- } else {
- /*
- * Following sequence of handling is as per each IRQ's
- * priority
- */
- if (val & CPR_INT_UP) {
- cpr_scale(drv, UP);
- } else if (val & CPR_INT_DOWN) {
- cpr_scale(drv, DOWN);
- } else if (val & CPR_INT_MIN) {
- cpr_irq_clr_nack(drv);
- } else if (val & CPR_INT_MAX) {
- cpr_irq_clr_nack(drv);
- } else if (val & CPR_INT_MID) {
- /* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */
- dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n");
- } else {
- dev_dbg(drv->dev,
- "IRQ occurred for unknown flag (%#08x)\n", val);
- }
-
- /* Save register values for the corner */
- cpr_corner_save(drv, drv->corner);
- }
-
- mutex_unlock(&drv->lock);
-
- return ret;
-}
-
-static int cpr_enable(struct cpr_drv *drv)
-{
- int ret;
-
- ret = regulator_enable(drv->vdd_apc);
- if (ret)
- return ret;
-
- mutex_lock(&drv->lock);
-
- if (cpr_is_allowed(drv) && drv->corner) {
- cpr_irq_clr(drv);
- cpr_corner_restore(drv, drv->corner);
- cpr_ctl_enable(drv, drv->corner);
- }
-
- mutex_unlock(&drv->lock);
-
- return 0;
-}
-
-static int cpr_disable(struct cpr_drv *drv)
-{
- mutex_lock(&drv->lock);
-
- if (cpr_is_allowed(drv)) {
- cpr_ctl_disable(drv);
- cpr_irq_clr(drv);
- }
-
- mutex_unlock(&drv->lock);
-
- return regulator_disable(drv->vdd_apc);
-}
-
-static int cpr_config(struct cpr_drv *drv)
-{
- int i;
- u32 val, gcnt;
- struct corner *corner;
- const struct cpr_desc *desc = drv->desc;
-
- /* Disable interrupt and CPR */
- cpr_write(drv, REG_RBIF_IRQ_EN(0), 0);
- cpr_write(drv, REG_RBCPR_CTL, 0);
-
- /* Program the default HW ceiling, floor and vlevel */
- val = (RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK)
- << RBIF_LIMIT_CEILING_SHIFT;
- val |= RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK;
- cpr_write(drv, REG_RBIF_LIMIT, val);
- cpr_write(drv, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT);
-
- /*
- * Clear the target quotient value and gate count of all
- * ring oscillators
- */
- for (i = 0; i < CPR_NUM_RING_OSC; i++)
- cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0);
-
- /* Init and save gcnt */
- gcnt = (drv->ref_clk_khz * desc->gcnt_us) / 1000;
- gcnt = gcnt & RBCPR_GCNT_TARGET_GCNT_MASK;
- gcnt <<= RBCPR_GCNT_TARGET_GCNT_SHIFT;
- drv->gcnt = gcnt;
-
- /* Program the delay count for the timer */
- val = (drv->ref_clk_khz * desc->timer_delay_us) / 1000;
- cpr_write(drv, REG_RBCPR_TIMER_INTERVAL, val);
- dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val,
- desc->timer_delay_us);
-
- /* Program Consecutive Up & Down */
- val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT;
- val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT;
- val |= desc->clamp_timer_interval << RBIF_TIMER_ADJ_CLAMP_INT_SHIFT;
- cpr_write(drv, REG_RBIF_TIMER_ADJUST, val);
-
- /* Program the control register */
- val = desc->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT;
- val |= desc->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT;
- val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE;
- val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN;
- cpr_write(drv, REG_RBCPR_CTL, val);
-
- for (i = 0; i < drv->num_corners; i++) {
- corner = &drv->corners[i];
- corner->save_ctl = val;
- corner->save_irq = CPR_INT_DEFAULT;
- }
-
- cpr_irq_set(drv, CPR_INT_DEFAULT);
-
- val = cpr_read(drv, REG_RBCPR_VERSION);
- if (val <= RBCPR_VER_2)
- drv->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS;
-
- return 0;
-}
-
-static int cpr_set_performance_state(struct generic_pm_domain *domain,
- unsigned int state)
-{
- struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd);
- struct corner *corner, *end;
- enum voltage_change_dir dir;
- int ret = 0, new_uV;
-
- mutex_lock(&drv->lock);
-
- dev_dbg(drv->dev, "%s: setting perf state: %u (prev state: %u)\n",
- __func__, state, cpr_get_cur_perf_state(drv));
-
- /*
- * Determine new corner we're going to.
- * Remove one since lowest performance state is 1.
- */
- corner = drv->corners + state - 1;
- end = &drv->corners[drv->num_corners - 1];
- if (corner > end || corner < drv->corners) {
- ret = -EINVAL;
- goto unlock;
- }
-
- /* Determine direction */
- if (drv->corner > corner)
- dir = DOWN;
- else if (drv->corner < corner)
- dir = UP;
- else
- dir = NO_CHANGE;
-
- if (cpr_is_allowed(drv))
- new_uV = corner->last_uV;
- else
- new_uV = corner->uV;
-
- if (cpr_is_allowed(drv))
- cpr_ctl_disable(drv);
-
- ret = cpr_scale_voltage(drv, corner, new_uV, dir);
- if (ret)
- goto unlock;
-
- if (cpr_is_allowed(drv)) {
- cpr_irq_clr(drv);
- if (drv->corner != corner)
- cpr_corner_restore(drv, corner);
- cpr_ctl_enable(drv, corner);
- }
-
- drv->corner = corner;
-
-unlock:
- mutex_unlock(&drv->lock);
-
- return ret;
-}
-
-static int cpr_read_efuse(struct device *dev, const char *cname, u32 *data)
-{
- struct nvmem_cell *cell;
- ssize_t len;
- char *ret;
- int i;
-
- *data = 0;
-
- cell = nvmem_cell_get(dev, cname);
- if (IS_ERR(cell)) {
- if (PTR_ERR(cell) != -EPROBE_DEFER)
- dev_err(dev, "undefined cell %s\n", cname);
- return PTR_ERR(cell);
- }
-
- ret = nvmem_cell_read(cell, &len);
- nvmem_cell_put(cell);
- if (IS_ERR(ret)) {
- dev_err(dev, "can't read cell %s\n", cname);
- return PTR_ERR(ret);
- }
-
- for (i = 0; i < len; i++)
- *data |= ret[i] << (8 * i);
-
- kfree(ret);
- dev_dbg(dev, "efuse read(%s) = %x, bytes %zd\n", cname, *data, len);
-
- return 0;
-}
-
-static int
-cpr_populate_ring_osc_idx(struct cpr_drv *drv)
-{
- struct fuse_corner *fuse = drv->fuse_corners;
- struct fuse_corner *end = fuse + drv->desc->num_fuse_corners;
- const struct cpr_fuse *fuses = drv->cpr_fuses;
- u32 data;
- int ret;
-
- for (; fuse < end; fuse++, fuses++) {
- ret = cpr_read_efuse(drv->dev, fuses->ring_osc,
- &data);
- if (ret)
- return ret;
- fuse->ring_osc_idx = data;
- }
-
- return 0;
-}
-
-static int cpr_read_fuse_uV(const struct cpr_desc *desc,
- const struct fuse_corner_data *fdata,
- const char *init_v_efuse,
- int step_volt,
- struct cpr_drv *drv)
-{
- int step_size_uV, steps, uV;
- u32 bits = 0;
- int ret;
-
- ret = cpr_read_efuse(drv->dev, init_v_efuse, &bits);
- if (ret)
- return ret;
-
- steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1);
- /* Not two's complement.. instead highest bit is sign bit */
- if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1))
- steps = -steps;
-
- step_size_uV = desc->cpr_fuses.init_voltage_step;
-
- uV = fdata->ref_uV + steps * step_size_uV;
- return DIV_ROUND_UP(uV, step_volt) * step_volt;
-}
-
-static int cpr_fuse_corner_init(struct cpr_drv *drv)
-{
- const struct cpr_desc *desc = drv->desc;
- const struct cpr_fuse *fuses = drv->cpr_fuses;
- const struct acc_desc *acc_desc = drv->acc_desc;
- int i;
- unsigned int step_volt;
- struct fuse_corner_data *fdata;
- struct fuse_corner *fuse, *end;
- int uV;
- const struct reg_sequence *accs;
- int ret;
-
- accs = acc_desc->settings;
-
- step_volt = regulator_get_linear_step(drv->vdd_apc);
- if (!step_volt)
- return -EINVAL;
-
- /* Populate fuse_corner members */
- fuse = drv->fuse_corners;
- end = &fuse[desc->num_fuse_corners - 1];
- fdata = desc->cpr_fuses.fuse_corner_data;
-
- for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) {
- /*
- * Update SoC voltages: platforms might choose a different
- * regulators than the one used to characterize the algorithms
- * (ie, init_voltage_step).
- */
- fdata->min_uV = roundup(fdata->min_uV, step_volt);
- fdata->max_uV = roundup(fdata->max_uV, step_volt);
-
- /* Populate uV */
- uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage,
- step_volt, drv);
- if (uV < 0)
- return uV;
-
- fuse->min_uV = fdata->min_uV;
- fuse->max_uV = fdata->max_uV;
- fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV);
-
- if (fuse == end) {
- /*
- * Allow the highest fuse corner's PVS voltage to
- * define the ceiling voltage for that corner in order
- * to support SoC's in which variable ceiling values
- * are required.
- */
- end->max_uV = max(end->max_uV, end->uV);
- }
-
- /* Populate target quotient by scaling */
- ret = cpr_read_efuse(drv->dev, fuses->quotient, &fuse->quot);
- if (ret)
- return ret;
-
- fuse->quot *= fdata->quot_scale;
- fuse->quot += fdata->quot_offset;
- fuse->quot += fdata->quot_adjust;
- fuse->step_quot = desc->step_quot[fuse->ring_osc_idx];
-
- /* Populate acc settings */
- fuse->accs = accs;
- fuse->num_accs = acc_desc->num_regs_per_fuse;
- accs += acc_desc->num_regs_per_fuse;
- }
-
- /*
- * Restrict all fuse corner PVS voltages based upon per corner
- * ceiling and floor voltages.
- */
- for (fuse = drv->fuse_corners, i = 0; fuse <= end; fuse++, i++) {
- if (fuse->uV > fuse->max_uV)
- fuse->uV = fuse->max_uV;
- else if (fuse->uV < fuse->min_uV)
- fuse->uV = fuse->min_uV;
-
- ret = regulator_is_supported_voltage(drv->vdd_apc,
- fuse->min_uV,
- fuse->min_uV);
- if (!ret) {
- dev_err(drv->dev,
- "min uV: %d (fuse corner: %d) not supported by regulator\n",
- fuse->min_uV, i);
- return -EINVAL;
- }
-
- ret = regulator_is_supported_voltage(drv->vdd_apc,
- fuse->max_uV,
- fuse->max_uV);
- if (!ret) {
- dev_err(drv->dev,
- "max uV: %d (fuse corner: %d) not supported by regulator\n",
- fuse->max_uV, i);
- return -EINVAL;
- }
-
- dev_dbg(drv->dev,
- "fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n",
- i, fuse->min_uV, fuse->uV, fuse->max_uV,
- fuse->ring_osc_idx, fuse->quot, fuse->step_quot);
- }
-
- return 0;
-}
-
-static int cpr_calculate_scaling(const char *quot_offset,
- struct cpr_drv *drv,
- const struct fuse_corner_data *fdata,
- const struct corner *corner)
-{
- u32 quot_diff = 0;
- unsigned long freq_diff;
- int scaling;
- const struct fuse_corner *fuse, *prev_fuse;
- int ret;
-
- fuse = corner->fuse_corner;
- prev_fuse = fuse - 1;
-
- if (quot_offset) {
- ret = cpr_read_efuse(drv->dev, quot_offset, &quot_diff);
- if (ret)
- return ret;
-
- quot_diff *= fdata->quot_offset_scale;
- quot_diff += fdata->quot_offset_adjust;
- } else {
- quot_diff = fuse->quot - prev_fuse->quot;
- }
-
- freq_diff = fuse->max_freq - prev_fuse->max_freq;
- freq_diff /= 1000000; /* Convert to MHz */
- scaling = 1000 * quot_diff / freq_diff;
- return min(scaling, fdata->max_quot_scale);
-}
-
-static int cpr_interpolate(const struct corner *corner, int step_volt,
- const struct fuse_corner_data *fdata)
-{
- unsigned long f_high, f_low, f_diff;
- int uV_high, uV_low, uV;
- u64 temp, temp_limit;
- const struct fuse_corner *fuse, *prev_fuse;
-
- fuse = corner->fuse_corner;
- prev_fuse = fuse - 1;
-
- f_high = fuse->max_freq;
- f_low = prev_fuse->max_freq;
- uV_high = fuse->uV;
- uV_low = prev_fuse->uV;
- f_diff = fuse->max_freq - corner->freq;
-
- /*
- * Don't interpolate in the wrong direction. This could happen
- * if the adjusted fuse voltage overlaps with the previous fuse's
- * adjusted voltage.
- */
- if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq)
- return corner->uV;
-
- temp = f_diff * (uV_high - uV_low);
- do_div(temp, f_high - f_low);
-
- /*
- * max_volt_scale has units of uV/MHz while freq values
- * have units of Hz. Divide by 1000000 to convert to.
- */
- temp_limit = f_diff * fdata->max_volt_scale;
- do_div(temp_limit, 1000000);
-
- uV = uV_high - min(temp, temp_limit);
- return roundup(uV, step_volt);
-}
-
-static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp)
-{
- struct device_node *np;
- unsigned int fuse_corner = 0;
-
- np = dev_pm_opp_get_of_node(opp);
- if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner))
- pr_err("%s: missing 'qcom,opp-fuse-level' property\n",
- __func__);
-
- of_node_put(np);
-
- return fuse_corner;
-}
-
-static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref,
- struct device *cpu_dev)
-{
- u64 rate = 0;
- struct device_node *ref_np;
- struct device_node *desc_np;
- struct device_node *child_np = NULL;
- struct device_node *child_req_np = NULL;
-
- desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
- if (!desc_np)
- return 0;
-
- ref_np = dev_pm_opp_get_of_node(ref);
- if (!ref_np)
- goto out_ref;
-
- do {
- of_node_put(child_req_np);
- child_np = of_get_next_available_child(desc_np, child_np);
- child_req_np = of_parse_phandle(child_np, "required-opps", 0);
- } while (child_np && child_req_np != ref_np);
-
- if (child_np && child_req_np == ref_np)
- of_property_read_u64(child_np, "opp-hz", &rate);
-
- of_node_put(child_req_np);
- of_node_put(child_np);
- of_node_put(ref_np);
-out_ref:
- of_node_put(desc_np);
-
- return (unsigned long) rate;
-}
-
-static int cpr_corner_init(struct cpr_drv *drv)
-{
- const struct cpr_desc *desc = drv->desc;
- const struct cpr_fuse *fuses = drv->cpr_fuses;
- int i, level, scaling = 0;
- unsigned int fnum, fc;
- const char *quot_offset;
- struct fuse_corner *fuse, *prev_fuse;
- struct corner *corner, *end;
- struct corner_data *cdata;
- const struct fuse_corner_data *fdata;
- bool apply_scaling;
- unsigned long freq_diff, freq_diff_mhz;
- unsigned long freq;
- int step_volt = regulator_get_linear_step(drv->vdd_apc);
- struct dev_pm_opp *opp;
-
- if (!step_volt)
- return -EINVAL;
-
- corner = drv->corners;
- end = &corner[drv->num_corners - 1];
-
- cdata = devm_kcalloc(drv->dev, drv->num_corners,
- sizeof(struct corner_data),
- GFP_KERNEL);
- if (!cdata)