18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2019, Linaro Limited
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/list.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/pm_domain.h>
218c2ecf20Sopenharmony_ci#include <linux/pm_opp.h>
228c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
238c2ecf20Sopenharmony_ci#include <linux/regmap.h>
248c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
258c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
268c2ecf20Sopenharmony_ci#include <linux/clk.h>
278c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Register Offsets for RB-CPR and Bit Definitions */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* RBCPR Version Register */
328c2ecf20Sopenharmony_ci#define REG_RBCPR_VERSION		0
338c2ecf20Sopenharmony_ci#define RBCPR_VER_2			0x02
348c2ecf20Sopenharmony_ci#define FLAGS_IGNORE_1ST_IRQ_STATUS	BIT(0)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* RBCPR Gate Count and Target Registers */
378c2ecf20Sopenharmony_ci#define REG_RBCPR_GCNT_TARGET(n)	(0x60 + 4 * (n))
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define RBCPR_GCNT_TARGET_TARGET_SHIFT	0
408c2ecf20Sopenharmony_ci#define RBCPR_GCNT_TARGET_TARGET_MASK	GENMASK(11, 0)
418c2ecf20Sopenharmony_ci#define RBCPR_GCNT_TARGET_GCNT_SHIFT	12
428c2ecf20Sopenharmony_ci#define RBCPR_GCNT_TARGET_GCNT_MASK	GENMASK(9, 0)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* RBCPR Timer Control */
458c2ecf20Sopenharmony_ci#define REG_RBCPR_TIMER_INTERVAL	0x44
468c2ecf20Sopenharmony_ci#define REG_RBIF_TIMER_ADJUST		0x4c
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CONS_UP_MASK	GENMASK(3, 0)
498c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CONS_UP_SHIFT	0
508c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CONS_DOWN_MASK	GENMASK(3, 0)
518c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT	4
528c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CLAMP_INT_MASK	GENMASK(7, 0)
538c2ecf20Sopenharmony_ci#define RBIF_TIMER_ADJ_CLAMP_INT_SHIFT	8
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* RBCPR Config Register */
568c2ecf20Sopenharmony_ci#define REG_RBIF_LIMIT			0x48
578c2ecf20Sopenharmony_ci#define RBIF_LIMIT_CEILING_MASK		GENMASK(5, 0)
588c2ecf20Sopenharmony_ci#define RBIF_LIMIT_CEILING_SHIFT	6
598c2ecf20Sopenharmony_ci#define RBIF_LIMIT_FLOOR_BITS		6
608c2ecf20Sopenharmony_ci#define RBIF_LIMIT_FLOOR_MASK		GENMASK(5, 0)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define RBIF_LIMIT_CEILING_DEFAULT	RBIF_LIMIT_CEILING_MASK
638c2ecf20Sopenharmony_ci#define RBIF_LIMIT_FLOOR_DEFAULT	0
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define REG_RBIF_SW_VLEVEL		0x94
668c2ecf20Sopenharmony_ci#define RBIF_SW_VLEVEL_DEFAULT		0x20
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define REG_RBCPR_STEP_QUOT		0x80
698c2ecf20Sopenharmony_ci#define RBCPR_STEP_QUOT_STEPQUOT_MASK	GENMASK(7, 0)
708c2ecf20Sopenharmony_ci#define RBCPR_STEP_QUOT_IDLE_CLK_MASK	GENMASK(3, 0)
718c2ecf20Sopenharmony_ci#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT	8
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* RBCPR Control Register */
748c2ecf20Sopenharmony_ci#define REG_RBCPR_CTL			0x90
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define RBCPR_CTL_LOOP_EN			BIT(0)
778c2ecf20Sopenharmony_ci#define RBCPR_CTL_TIMER_EN			BIT(3)
788c2ecf20Sopenharmony_ci#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN		BIT(5)
798c2ecf20Sopenharmony_ci#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN	BIT(6)
808c2ecf20Sopenharmony_ci#define RBCPR_CTL_COUNT_MODE			BIT(10)
818c2ecf20Sopenharmony_ci#define RBCPR_CTL_UP_THRESHOLD_MASK	GENMASK(3, 0)
828c2ecf20Sopenharmony_ci#define RBCPR_CTL_UP_THRESHOLD_SHIFT	24
838c2ecf20Sopenharmony_ci#define RBCPR_CTL_DN_THRESHOLD_MASK	GENMASK(3, 0)
848c2ecf20Sopenharmony_ci#define RBCPR_CTL_DN_THRESHOLD_SHIFT	28
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* RBCPR Ack/Nack Response */
878c2ecf20Sopenharmony_ci#define REG_RBIF_CONT_ACK_CMD		0x98
888c2ecf20Sopenharmony_ci#define REG_RBIF_CONT_NACK_CMD		0x9c
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* RBCPR Result status Register */
918c2ecf20Sopenharmony_ci#define REG_RBCPR_RESULT_0		0xa0
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_BUSY_SHIFT	19
948c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_BUSY_MASK		BIT(RBCPR_RESULT0_BUSY_SHIFT)
958c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_ERROR_LT0_SHIFT	18
968c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_ERROR_SHIFT	6
978c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_ERROR_MASK	GENMASK(11, 0)
988c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_ERROR_STEPS_SHIFT	2
998c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_ERROR_STEPS_MASK	GENMASK(3, 0)
1008c2ecf20Sopenharmony_ci#define RBCPR_RESULT0_STEP_UP_SHIFT	1
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* RBCPR Interrupt Control Register */
1038c2ecf20Sopenharmony_ci#define REG_RBIF_IRQ_EN(n)		(0x100 + 4 * (n))
1048c2ecf20Sopenharmony_ci#define REG_RBIF_IRQ_CLEAR		0x110
1058c2ecf20Sopenharmony_ci#define REG_RBIF_IRQ_STATUS		0x114
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define CPR_INT_DONE		BIT(0)
1088c2ecf20Sopenharmony_ci#define CPR_INT_MIN		BIT(1)
1098c2ecf20Sopenharmony_ci#define CPR_INT_DOWN		BIT(2)
1108c2ecf20Sopenharmony_ci#define CPR_INT_MID		BIT(3)
1118c2ecf20Sopenharmony_ci#define CPR_INT_UP		BIT(4)
1128c2ecf20Sopenharmony_ci#define CPR_INT_MAX		BIT(5)
1138c2ecf20Sopenharmony_ci#define CPR_INT_CLAMP		BIT(6)
1148c2ecf20Sopenharmony_ci#define CPR_INT_ALL	(CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \
1158c2ecf20Sopenharmony_ci			CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP)
1168c2ecf20Sopenharmony_ci#define CPR_INT_DEFAULT	(CPR_INT_UP | CPR_INT_DOWN)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define CPR_NUM_RING_OSC	8
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* CPR eFuse parameters */
1218c2ecf20Sopenharmony_ci#define CPR_FUSE_TARGET_QUOT_BITS_MASK	GENMASK(11, 0)
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define CPR_FUSE_MIN_QUOT_DIFF		50
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define FUSE_REVISION_UNKNOWN		(-1)
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cienum voltage_change_dir {
1288c2ecf20Sopenharmony_ci	NO_CHANGE,
1298c2ecf20Sopenharmony_ci	DOWN,
1308c2ecf20Sopenharmony_ci	UP,
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistruct cpr_fuse {
1348c2ecf20Sopenharmony_ci	char *ring_osc;
1358c2ecf20Sopenharmony_ci	char *init_voltage;
1368c2ecf20Sopenharmony_ci	char *quotient;
1378c2ecf20Sopenharmony_ci	char *quotient_offset;
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct fuse_corner_data {
1418c2ecf20Sopenharmony_ci	int ref_uV;
1428c2ecf20Sopenharmony_ci	int max_uV;
1438c2ecf20Sopenharmony_ci	int min_uV;
1448c2ecf20Sopenharmony_ci	int max_volt_scale;
1458c2ecf20Sopenharmony_ci	int max_quot_scale;
1468c2ecf20Sopenharmony_ci	/* fuse quot */
1478c2ecf20Sopenharmony_ci	int quot_offset;
1488c2ecf20Sopenharmony_ci	int quot_scale;
1498c2ecf20Sopenharmony_ci	int quot_adjust;
1508c2ecf20Sopenharmony_ci	/* fuse quot_offset */
1518c2ecf20Sopenharmony_ci	int quot_offset_scale;
1528c2ecf20Sopenharmony_ci	int quot_offset_adjust;
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistruct cpr_fuses {
1568c2ecf20Sopenharmony_ci	int init_voltage_step;
1578c2ecf20Sopenharmony_ci	int init_voltage_width;
1588c2ecf20Sopenharmony_ci	struct fuse_corner_data *fuse_corner_data;
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistruct corner_data {
1628c2ecf20Sopenharmony_ci	unsigned int fuse_corner;
1638c2ecf20Sopenharmony_ci	unsigned long freq;
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistruct cpr_desc {
1678c2ecf20Sopenharmony_ci	unsigned int num_fuse_corners;
1688c2ecf20Sopenharmony_ci	int min_diff_quot;
1698c2ecf20Sopenharmony_ci	int *step_quot;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	unsigned int		timer_delay_us;
1728c2ecf20Sopenharmony_ci	unsigned int		timer_cons_up;
1738c2ecf20Sopenharmony_ci	unsigned int		timer_cons_down;
1748c2ecf20Sopenharmony_ci	unsigned int		up_threshold;
1758c2ecf20Sopenharmony_ci	unsigned int		down_threshold;
1768c2ecf20Sopenharmony_ci	unsigned int		idle_clocks;
1778c2ecf20Sopenharmony_ci	unsigned int		gcnt_us;
1788c2ecf20Sopenharmony_ci	unsigned int		vdd_apc_step_up_limit;
1798c2ecf20Sopenharmony_ci	unsigned int		vdd_apc_step_down_limit;
1808c2ecf20Sopenharmony_ci	unsigned int		clamp_timer_interval;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	struct cpr_fuses cpr_fuses;
1838c2ecf20Sopenharmony_ci	bool reduce_to_fuse_uV;
1848c2ecf20Sopenharmony_ci	bool reduce_to_corner_uV;
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistruct acc_desc {
1888c2ecf20Sopenharmony_ci	unsigned int	enable_reg;
1898c2ecf20Sopenharmony_ci	u32		enable_mask;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	struct reg_sequence	*config;
1928c2ecf20Sopenharmony_ci	struct reg_sequence	*settings;
1938c2ecf20Sopenharmony_ci	int			num_regs_per_fuse;
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistruct cpr_acc_desc {
1978c2ecf20Sopenharmony_ci	const struct cpr_desc *cpr_desc;
1988c2ecf20Sopenharmony_ci	const struct acc_desc *acc_desc;
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistruct fuse_corner {
2028c2ecf20Sopenharmony_ci	int min_uV;
2038c2ecf20Sopenharmony_ci	int max_uV;
2048c2ecf20Sopenharmony_ci	int uV;
2058c2ecf20Sopenharmony_ci	int quot;
2068c2ecf20Sopenharmony_ci	int step_quot;
2078c2ecf20Sopenharmony_ci	const struct reg_sequence *accs;
2088c2ecf20Sopenharmony_ci	int num_accs;
2098c2ecf20Sopenharmony_ci	unsigned long max_freq;
2108c2ecf20Sopenharmony_ci	u8 ring_osc_idx;
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistruct corner {
2148c2ecf20Sopenharmony_ci	int min_uV;
2158c2ecf20Sopenharmony_ci	int max_uV;
2168c2ecf20Sopenharmony_ci	int uV;
2178c2ecf20Sopenharmony_ci	int last_uV;
2188c2ecf20Sopenharmony_ci	int quot_adjust;
2198c2ecf20Sopenharmony_ci	u32 save_ctl;
2208c2ecf20Sopenharmony_ci	u32 save_irq;
2218c2ecf20Sopenharmony_ci	unsigned long freq;
2228c2ecf20Sopenharmony_ci	struct fuse_corner *fuse_corner;
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistruct cpr_drv {
2268c2ecf20Sopenharmony_ci	unsigned int		num_corners;
2278c2ecf20Sopenharmony_ci	unsigned int		ref_clk_khz;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	struct generic_pm_domain pd;
2308c2ecf20Sopenharmony_ci	struct device		*dev;
2318c2ecf20Sopenharmony_ci	struct device		*attached_cpu_dev;
2328c2ecf20Sopenharmony_ci	struct mutex		lock;
2338c2ecf20Sopenharmony_ci	void __iomem		*base;
2348c2ecf20Sopenharmony_ci	struct corner		*corner;
2358c2ecf20Sopenharmony_ci	struct regulator	*vdd_apc;
2368c2ecf20Sopenharmony_ci	struct clk		*cpu_clk;
2378c2ecf20Sopenharmony_ci	struct regmap		*tcsr;
2388c2ecf20Sopenharmony_ci	bool			loop_disabled;
2398c2ecf20Sopenharmony_ci	u32			gcnt;
2408c2ecf20Sopenharmony_ci	unsigned long		flags;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	struct fuse_corner	*fuse_corners;
2438c2ecf20Sopenharmony_ci	struct corner		*corners;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	const struct cpr_desc *desc;
2468c2ecf20Sopenharmony_ci	const struct acc_desc *acc_desc;
2478c2ecf20Sopenharmony_ci	const struct cpr_fuse *cpr_fuses;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	struct dentry *debugfs;
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic bool cpr_is_allowed(struct cpr_drv *drv)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	return !drv->loop_disabled;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void cpr_write(struct cpr_drv *drv, u32 offset, u32 value)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	writel_relaxed(value, drv->base + offset);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic u32 cpr_read(struct cpr_drv *drv, u32 offset)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	return readl_relaxed(drv->base + offset);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void
2688c2ecf20Sopenharmony_cicpr_masked_write(struct cpr_drv *drv, u32 offset, u32 mask, u32 value)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	u32 val;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	val = readl_relaxed(drv->base + offset);
2738c2ecf20Sopenharmony_ci	val &= ~mask;
2748c2ecf20Sopenharmony_ci	val |= value & mask;
2758c2ecf20Sopenharmony_ci	writel_relaxed(val, drv->base + offset);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void cpr_irq_clr(struct cpr_drv *drv)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void cpr_irq_clr_nack(struct cpr_drv *drv)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	cpr_irq_clr(drv);
2868c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic void cpr_irq_clr_ack(struct cpr_drv *drv)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	cpr_irq_clr(drv);
2928c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic void cpr_irq_set(struct cpr_drv *drv, u32 int_bits)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_IRQ_EN(0), int_bits);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void cpr_ctl_modify(struct cpr_drv *drv, u32 mask, u32 value)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	cpr_masked_write(drv, REG_RBCPR_CTL, mask, value);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void cpr_ctl_enable(struct cpr_drv *drv, struct corner *corner)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	u32 val, mask;
3088c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Program Consecutive Up & Down */
3118c2ecf20Sopenharmony_ci	val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT;
3128c2ecf20Sopenharmony_ci	val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT;
3138c2ecf20Sopenharmony_ci	mask = RBIF_TIMER_ADJ_CONS_UP_MASK | RBIF_TIMER_ADJ_CONS_DOWN_MASK;
3148c2ecf20Sopenharmony_ci	cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, mask, val);
3158c2ecf20Sopenharmony_ci	cpr_masked_write(drv, REG_RBCPR_CTL,
3168c2ecf20Sopenharmony_ci			 RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN |
3178c2ecf20Sopenharmony_ci			 RBCPR_CTL_SW_AUTO_CONT_ACK_EN,
3188c2ecf20Sopenharmony_ci			 corner->save_ctl);
3198c2ecf20Sopenharmony_ci	cpr_irq_set(drv, corner->save_irq);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv) && corner->max_uV > corner->min_uV)
3228c2ecf20Sopenharmony_ci		val = RBCPR_CTL_LOOP_EN;
3238c2ecf20Sopenharmony_ci	else
3248c2ecf20Sopenharmony_ci		val = 0;
3258c2ecf20Sopenharmony_ci	cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, val);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void cpr_ctl_disable(struct cpr_drv *drv)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	cpr_irq_set(drv, 0);
3318c2ecf20Sopenharmony_ci	cpr_ctl_modify(drv, RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN |
3328c2ecf20Sopenharmony_ci		       RBCPR_CTL_SW_AUTO_CONT_ACK_EN, 0);
3338c2ecf20Sopenharmony_ci	cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST,
3348c2ecf20Sopenharmony_ci			 RBIF_TIMER_ADJ_CONS_UP_MASK |
3358c2ecf20Sopenharmony_ci			 RBIF_TIMER_ADJ_CONS_DOWN_MASK, 0);
3368c2ecf20Sopenharmony_ci	cpr_irq_clr(drv);
3378c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1);
3388c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1);
3398c2ecf20Sopenharmony_ci	cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, 0);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic bool cpr_ctl_is_enabled(struct cpr_drv *drv)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	u32 reg_val;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	reg_val = cpr_read(drv, REG_RBCPR_CTL);
3478c2ecf20Sopenharmony_ci	return reg_val & RBCPR_CTL_LOOP_EN;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic bool cpr_ctl_is_busy(struct cpr_drv *drv)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	u32 reg_val;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	reg_val = cpr_read(drv, REG_RBCPR_RESULT_0);
3558c2ecf20Sopenharmony_ci	return reg_val & RBCPR_RESULT0_BUSY_MASK;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic void cpr_corner_save(struct cpr_drv *drv, struct corner *corner)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	corner->save_ctl = cpr_read(drv, REG_RBCPR_CTL);
3618c2ecf20Sopenharmony_ci	corner->save_irq = cpr_read(drv, REG_RBIF_IRQ_EN(0));
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic void cpr_corner_restore(struct cpr_drv *drv, struct corner *corner)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	u32 gcnt, ctl, irq, ro_sel, step_quot;
3678c2ecf20Sopenharmony_ci	struct fuse_corner *fuse = corner->fuse_corner;
3688c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
3698c2ecf20Sopenharmony_ci	int i;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ro_sel = fuse->ring_osc_idx;
3728c2ecf20Sopenharmony_ci	gcnt = drv->gcnt;
3738c2ecf20Sopenharmony_ci	gcnt |= fuse->quot - corner->quot_adjust;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* Program the step quotient and idle clocks */
3768c2ecf20Sopenharmony_ci	step_quot = desc->idle_clocks << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT;
3778c2ecf20Sopenharmony_ci	step_quot |= fuse->step_quot & RBCPR_STEP_QUOT_STEPQUOT_MASK;
3788c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_STEP_QUOT, step_quot);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Clear the target quotient value and gate count of all ROs */
3818c2ecf20Sopenharmony_ci	for (i = 0; i < CPR_NUM_RING_OSC; i++)
3828c2ecf20Sopenharmony_ci		cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt);
3858c2ecf20Sopenharmony_ci	ctl = corner->save_ctl;
3868c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_CTL, ctl);
3878c2ecf20Sopenharmony_ci	irq = corner->save_irq;
3888c2ecf20Sopenharmony_ci	cpr_irq_set(drv, irq);
3898c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "gcnt = %#08x, ctl = %#08x, irq = %#08x\n", gcnt,
3908c2ecf20Sopenharmony_ci		ctl, irq);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic void cpr_set_acc(struct regmap *tcsr, struct fuse_corner *f,
3948c2ecf20Sopenharmony_ci			struct fuse_corner *end)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	if (f == end)
3978c2ecf20Sopenharmony_ci		return;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (f < end) {
4008c2ecf20Sopenharmony_ci		for (f += 1; f <= end; f++)
4018c2ecf20Sopenharmony_ci			regmap_multi_reg_write(tcsr, f->accs, f->num_accs);
4028c2ecf20Sopenharmony_ci	} else {
4038c2ecf20Sopenharmony_ci		for (f -= 1; f >= end; f--)
4048c2ecf20Sopenharmony_ci			regmap_multi_reg_write(tcsr, f->accs, f->num_accs);
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int cpr_pre_voltage(struct cpr_drv *drv,
4098c2ecf20Sopenharmony_ci			   struct fuse_corner *fuse_corner,
4108c2ecf20Sopenharmony_ci			   enum voltage_change_dir dir)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (drv->tcsr && dir == DOWN)
4158c2ecf20Sopenharmony_ci		cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int cpr_post_voltage(struct cpr_drv *drv,
4218c2ecf20Sopenharmony_ci			    struct fuse_corner *fuse_corner,
4228c2ecf20Sopenharmony_ci			    enum voltage_change_dir dir)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (drv->tcsr && dir == UP)
4278c2ecf20Sopenharmony_ci		cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int cpr_scale_voltage(struct cpr_drv *drv, struct corner *corner,
4338c2ecf20Sopenharmony_ci			     int new_uV, enum voltage_change_dir dir)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	int ret;
4368c2ecf20Sopenharmony_ci	struct fuse_corner *fuse_corner = corner->fuse_corner;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = cpr_pre_voltage(drv, fuse_corner, dir);
4398c2ecf20Sopenharmony_ci	if (ret)
4408c2ecf20Sopenharmony_ci		return ret;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	ret = regulator_set_voltage(drv->vdd_apc, new_uV, new_uV);
4438c2ecf20Sopenharmony_ci	if (ret) {
4448c2ecf20Sopenharmony_ci		dev_err_ratelimited(drv->dev, "failed to set apc voltage %d\n",
4458c2ecf20Sopenharmony_ci				    new_uV);
4468c2ecf20Sopenharmony_ci		return ret;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ret = cpr_post_voltage(drv, fuse_corner, dir);
4508c2ecf20Sopenharmony_ci	if (ret)
4518c2ecf20Sopenharmony_ci		return ret;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic unsigned int cpr_get_cur_perf_state(struct cpr_drv *drv)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	return drv->corner ? drv->corner - drv->corners + 1 : 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int cpr_scale(struct cpr_drv *drv, enum voltage_change_dir dir)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	u32 val, error_steps, reg_mask;
4648c2ecf20Sopenharmony_ci	int last_uV, new_uV, step_uV, ret;
4658c2ecf20Sopenharmony_ci	struct corner *corner;
4668c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (dir != UP && dir != DOWN)
4698c2ecf20Sopenharmony_ci		return 0;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	step_uV = regulator_get_linear_step(drv->vdd_apc);
4728c2ecf20Sopenharmony_ci	if (!step_uV)
4738c2ecf20Sopenharmony_ci		return -EINVAL;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	corner = drv->corner;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	val = cpr_read(drv, REG_RBCPR_RESULT_0);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	error_steps = val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT;
4808c2ecf20Sopenharmony_ci	error_steps &= RBCPR_RESULT0_ERROR_STEPS_MASK;
4818c2ecf20Sopenharmony_ci	last_uV = corner->last_uV;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (dir == UP) {
4848c2ecf20Sopenharmony_ci		if (desc->clamp_timer_interval &&
4858c2ecf20Sopenharmony_ci		    error_steps < desc->up_threshold) {
4868c2ecf20Sopenharmony_ci			/*
4878c2ecf20Sopenharmony_ci			 * Handle the case where another measurement started
4888c2ecf20Sopenharmony_ci			 * after the interrupt was triggered due to a core
4898c2ecf20Sopenharmony_ci			 * exiting from power collapse.
4908c2ecf20Sopenharmony_ci			 */
4918c2ecf20Sopenharmony_ci			error_steps = max(desc->up_threshold,
4928c2ecf20Sopenharmony_ci					  desc->vdd_apc_step_up_limit);
4938c2ecf20Sopenharmony_ci		}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		if (last_uV >= corner->max_uV) {
4968c2ecf20Sopenharmony_ci			cpr_irq_clr_nack(drv);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci			/* Maximize the UP threshold */
4998c2ecf20Sopenharmony_ci			reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
5008c2ecf20Sopenharmony_ci			reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
5018c2ecf20Sopenharmony_ci			val = reg_mask;
5028c2ecf20Sopenharmony_ci			cpr_ctl_modify(drv, reg_mask, val);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci			/* Disable UP interrupt */
5058c2ecf20Sopenharmony_ci			cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_UP);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			return 0;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		if (error_steps > desc->vdd_apc_step_up_limit)
5118c2ecf20Sopenharmony_ci			error_steps = desc->vdd_apc_step_up_limit;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		/* Calculate new voltage */
5148c2ecf20Sopenharmony_ci		new_uV = last_uV + error_steps * step_uV;
5158c2ecf20Sopenharmony_ci		new_uV = min(new_uV, corner->max_uV);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		dev_dbg(drv->dev,
5188c2ecf20Sopenharmony_ci			"UP: -> new_uV: %d last_uV: %d perf state: %u\n",
5198c2ecf20Sopenharmony_ci			new_uV, last_uV, cpr_get_cur_perf_state(drv));
5208c2ecf20Sopenharmony_ci	} else {
5218c2ecf20Sopenharmony_ci		if (desc->clamp_timer_interval &&
5228c2ecf20Sopenharmony_ci		    error_steps < desc->down_threshold) {
5238c2ecf20Sopenharmony_ci			/*
5248c2ecf20Sopenharmony_ci			 * Handle the case where another measurement started
5258c2ecf20Sopenharmony_ci			 * after the interrupt was triggered due to a core
5268c2ecf20Sopenharmony_ci			 * exiting from power collapse.
5278c2ecf20Sopenharmony_ci			 */
5288c2ecf20Sopenharmony_ci			error_steps = max(desc->down_threshold,
5298c2ecf20Sopenharmony_ci					  desc->vdd_apc_step_down_limit);
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (last_uV <= corner->min_uV) {
5338c2ecf20Sopenharmony_ci			cpr_irq_clr_nack(drv);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci			/* Enable auto nack down */
5368c2ecf20Sopenharmony_ci			reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
5378c2ecf20Sopenharmony_ci			val = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			cpr_ctl_modify(drv, reg_mask, val);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci			/* Disable DOWN interrupt */
5428c2ecf20Sopenharmony_ci			cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_DOWN);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci			return 0;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		if (error_steps > desc->vdd_apc_step_down_limit)
5488c2ecf20Sopenharmony_ci			error_steps = desc->vdd_apc_step_down_limit;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/* Calculate new voltage */
5518c2ecf20Sopenharmony_ci		new_uV = last_uV - error_steps * step_uV;
5528c2ecf20Sopenharmony_ci		new_uV = max(new_uV, corner->min_uV);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		dev_dbg(drv->dev,
5558c2ecf20Sopenharmony_ci			"DOWN: -> new_uV: %d last_uV: %d perf state: %u\n",
5568c2ecf20Sopenharmony_ci			new_uV, last_uV, cpr_get_cur_perf_state(drv));
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	ret = cpr_scale_voltage(drv, corner, new_uV, dir);
5608c2ecf20Sopenharmony_ci	if (ret) {
5618c2ecf20Sopenharmony_ci		cpr_irq_clr_nack(drv);
5628c2ecf20Sopenharmony_ci		return ret;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci	drv->corner->last_uV = new_uV;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (dir == UP) {
5678c2ecf20Sopenharmony_ci		/* Disable auto nack down */
5688c2ecf20Sopenharmony_ci		reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
5698c2ecf20Sopenharmony_ci		val = 0;
5708c2ecf20Sopenharmony_ci	} else {
5718c2ecf20Sopenharmony_ci		/* Restore default threshold for UP */
5728c2ecf20Sopenharmony_ci		reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
5738c2ecf20Sopenharmony_ci		reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
5748c2ecf20Sopenharmony_ci		val = desc->up_threshold;
5758c2ecf20Sopenharmony_ci		val <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	cpr_ctl_modify(drv, reg_mask, val);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/* Re-enable default interrupts */
5818c2ecf20Sopenharmony_ci	cpr_irq_set(drv, CPR_INT_DEFAULT);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Ack */
5848c2ecf20Sopenharmony_ci	cpr_irq_clr_ack(drv);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic irqreturn_t cpr_irq_handler(int irq, void *dev)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct cpr_drv *drv = dev;
5928c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
5938c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_HANDLED;
5948c2ecf20Sopenharmony_ci	u32 val;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	mutex_lock(&drv->lock);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	val = cpr_read(drv, REG_RBIF_IRQ_STATUS);
5998c2ecf20Sopenharmony_ci	if (drv->flags & FLAGS_IGNORE_1ST_IRQ_STATUS)
6008c2ecf20Sopenharmony_ci		val = cpr_read(drv, REG_RBIF_IRQ_STATUS);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "IRQ_STATUS = %#02x\n", val);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (!cpr_ctl_is_enabled(drv)) {
6058c2ecf20Sopenharmony_ci		dev_dbg(drv->dev, "CPR is disabled\n");
6068c2ecf20Sopenharmony_ci		ret = IRQ_NONE;
6078c2ecf20Sopenharmony_ci	} else if (cpr_ctl_is_busy(drv) && !desc->clamp_timer_interval) {
6088c2ecf20Sopenharmony_ci		dev_dbg(drv->dev, "CPR measurement is not ready\n");
6098c2ecf20Sopenharmony_ci	} else if (!cpr_is_allowed(drv)) {
6108c2ecf20Sopenharmony_ci		val = cpr_read(drv, REG_RBCPR_CTL);
6118c2ecf20Sopenharmony_ci		dev_err_ratelimited(drv->dev,
6128c2ecf20Sopenharmony_ci				    "Interrupt broken? RBCPR_CTL = %#02x\n",
6138c2ecf20Sopenharmony_ci				    val);
6148c2ecf20Sopenharmony_ci		ret = IRQ_NONE;
6158c2ecf20Sopenharmony_ci	} else {
6168c2ecf20Sopenharmony_ci		/*
6178c2ecf20Sopenharmony_ci		 * Following sequence of handling is as per each IRQ's
6188c2ecf20Sopenharmony_ci		 * priority
6198c2ecf20Sopenharmony_ci		 */
6208c2ecf20Sopenharmony_ci		if (val & CPR_INT_UP) {
6218c2ecf20Sopenharmony_ci			cpr_scale(drv, UP);
6228c2ecf20Sopenharmony_ci		} else if (val & CPR_INT_DOWN) {
6238c2ecf20Sopenharmony_ci			cpr_scale(drv, DOWN);
6248c2ecf20Sopenharmony_ci		} else if (val & CPR_INT_MIN) {
6258c2ecf20Sopenharmony_ci			cpr_irq_clr_nack(drv);
6268c2ecf20Sopenharmony_ci		} else if (val & CPR_INT_MAX) {
6278c2ecf20Sopenharmony_ci			cpr_irq_clr_nack(drv);
6288c2ecf20Sopenharmony_ci		} else if (val & CPR_INT_MID) {
6298c2ecf20Sopenharmony_ci			/* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */
6308c2ecf20Sopenharmony_ci			dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n");
6318c2ecf20Sopenharmony_ci		} else {
6328c2ecf20Sopenharmony_ci			dev_dbg(drv->dev,
6338c2ecf20Sopenharmony_ci				"IRQ occurred for unknown flag (%#08x)\n", val);
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		/* Save register values for the corner */
6378c2ecf20Sopenharmony_ci		cpr_corner_save(drv, drv->corner);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	mutex_unlock(&drv->lock);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	return ret;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic int cpr_enable(struct cpr_drv *drv)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	int ret;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	ret = regulator_enable(drv->vdd_apc);
6508c2ecf20Sopenharmony_ci	if (ret)
6518c2ecf20Sopenharmony_ci		return ret;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	mutex_lock(&drv->lock);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv) && drv->corner) {
6568c2ecf20Sopenharmony_ci		cpr_irq_clr(drv);
6578c2ecf20Sopenharmony_ci		cpr_corner_restore(drv, drv->corner);
6588c2ecf20Sopenharmony_ci		cpr_ctl_enable(drv, drv->corner);
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	mutex_unlock(&drv->lock);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return 0;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int cpr_disable(struct cpr_drv *drv)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	mutex_lock(&drv->lock);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv)) {
6718c2ecf20Sopenharmony_ci		cpr_ctl_disable(drv);
6728c2ecf20Sopenharmony_ci		cpr_irq_clr(drv);
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	mutex_unlock(&drv->lock);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	return regulator_disable(drv->vdd_apc);
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic int cpr_config(struct cpr_drv *drv)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	int i;
6838c2ecf20Sopenharmony_ci	u32 val, gcnt;
6848c2ecf20Sopenharmony_ci	struct corner *corner;
6858c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	/* Disable interrupt and CPR */
6888c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_IRQ_EN(0), 0);
6898c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_CTL, 0);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* Program the default HW ceiling, floor and vlevel */
6928c2ecf20Sopenharmony_ci	val = (RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK)
6938c2ecf20Sopenharmony_ci		<< RBIF_LIMIT_CEILING_SHIFT;
6948c2ecf20Sopenharmony_ci	val |= RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK;
6958c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_LIMIT, val);
6968c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/*
6998c2ecf20Sopenharmony_ci	 * Clear the target quotient value and gate count of all
7008c2ecf20Sopenharmony_ci	 * ring oscillators
7018c2ecf20Sopenharmony_ci	 */
7028c2ecf20Sopenharmony_ci	for (i = 0; i < CPR_NUM_RING_OSC; i++)
7038c2ecf20Sopenharmony_ci		cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* Init and save gcnt */
7068c2ecf20Sopenharmony_ci	gcnt = (drv->ref_clk_khz * desc->gcnt_us) / 1000;
7078c2ecf20Sopenharmony_ci	gcnt = gcnt & RBCPR_GCNT_TARGET_GCNT_MASK;
7088c2ecf20Sopenharmony_ci	gcnt <<= RBCPR_GCNT_TARGET_GCNT_SHIFT;
7098c2ecf20Sopenharmony_ci	drv->gcnt = gcnt;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* Program the delay count for the timer */
7128c2ecf20Sopenharmony_ci	val = (drv->ref_clk_khz * desc->timer_delay_us) / 1000;
7138c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_TIMER_INTERVAL, val);
7148c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val,
7158c2ecf20Sopenharmony_ci		desc->timer_delay_us);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Program Consecutive Up & Down */
7188c2ecf20Sopenharmony_ci	val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT;
7198c2ecf20Sopenharmony_ci	val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT;
7208c2ecf20Sopenharmony_ci	val |= desc->clamp_timer_interval << RBIF_TIMER_ADJ_CLAMP_INT_SHIFT;
7218c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBIF_TIMER_ADJUST, val);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* Program the control register */
7248c2ecf20Sopenharmony_ci	val = desc->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT;
7258c2ecf20Sopenharmony_ci	val |= desc->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT;
7268c2ecf20Sopenharmony_ci	val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE;
7278c2ecf20Sopenharmony_ci	val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN;
7288c2ecf20Sopenharmony_ci	cpr_write(drv, REG_RBCPR_CTL, val);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	for (i = 0; i < drv->num_corners; i++) {
7318c2ecf20Sopenharmony_ci		corner = &drv->corners[i];
7328c2ecf20Sopenharmony_ci		corner->save_ctl = val;
7338c2ecf20Sopenharmony_ci		corner->save_irq = CPR_INT_DEFAULT;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	cpr_irq_set(drv, CPR_INT_DEFAULT);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	val = cpr_read(drv, REG_RBCPR_VERSION);
7398c2ecf20Sopenharmony_ci	if (val <= RBCPR_VER_2)
7408c2ecf20Sopenharmony_ci		drv->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	return 0;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic int cpr_set_performance_state(struct generic_pm_domain *domain,
7468c2ecf20Sopenharmony_ci				     unsigned int state)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd);
7498c2ecf20Sopenharmony_ci	struct corner *corner, *end;
7508c2ecf20Sopenharmony_ci	enum voltage_change_dir dir;
7518c2ecf20Sopenharmony_ci	int ret = 0, new_uV;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	mutex_lock(&drv->lock);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "%s: setting perf state: %u (prev state: %u)\n",
7568c2ecf20Sopenharmony_ci		__func__, state, cpr_get_cur_perf_state(drv));
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/*
7598c2ecf20Sopenharmony_ci	 * Determine new corner we're going to.
7608c2ecf20Sopenharmony_ci	 * Remove one since lowest performance state is 1.
7618c2ecf20Sopenharmony_ci	 */
7628c2ecf20Sopenharmony_ci	corner = drv->corners + state - 1;
7638c2ecf20Sopenharmony_ci	end = &drv->corners[drv->num_corners - 1];
7648c2ecf20Sopenharmony_ci	if (corner > end || corner < drv->corners) {
7658c2ecf20Sopenharmony_ci		ret = -EINVAL;
7668c2ecf20Sopenharmony_ci		goto unlock;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Determine direction */
7708c2ecf20Sopenharmony_ci	if (drv->corner > corner)
7718c2ecf20Sopenharmony_ci		dir = DOWN;
7728c2ecf20Sopenharmony_ci	else if (drv->corner < corner)
7738c2ecf20Sopenharmony_ci		dir = UP;
7748c2ecf20Sopenharmony_ci	else
7758c2ecf20Sopenharmony_ci		dir = NO_CHANGE;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv))
7788c2ecf20Sopenharmony_ci		new_uV = corner->last_uV;
7798c2ecf20Sopenharmony_ci	else
7808c2ecf20Sopenharmony_ci		new_uV = corner->uV;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv))
7838c2ecf20Sopenharmony_ci		cpr_ctl_disable(drv);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	ret = cpr_scale_voltage(drv, corner, new_uV, dir);
7868c2ecf20Sopenharmony_ci	if (ret)
7878c2ecf20Sopenharmony_ci		goto unlock;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv)) {
7908c2ecf20Sopenharmony_ci		cpr_irq_clr(drv);
7918c2ecf20Sopenharmony_ci		if (drv->corner != corner)
7928c2ecf20Sopenharmony_ci			cpr_corner_restore(drv, corner);
7938c2ecf20Sopenharmony_ci		cpr_ctl_enable(drv, corner);
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	drv->corner = corner;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ciunlock:
7998c2ecf20Sopenharmony_ci	mutex_unlock(&drv->lock);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return ret;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic int cpr_read_efuse(struct device *dev, const char *cname, u32 *data)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
8078c2ecf20Sopenharmony_ci	ssize_t len;
8088c2ecf20Sopenharmony_ci	char *ret;
8098c2ecf20Sopenharmony_ci	int i;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	*data = 0;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	cell = nvmem_cell_get(dev, cname);
8148c2ecf20Sopenharmony_ci	if (IS_ERR(cell)) {
8158c2ecf20Sopenharmony_ci		if (PTR_ERR(cell) != -EPROBE_DEFER)
8168c2ecf20Sopenharmony_ci			dev_err(dev, "undefined cell %s\n", cname);
8178c2ecf20Sopenharmony_ci		return PTR_ERR(cell);
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	ret = nvmem_cell_read(cell, &len);
8218c2ecf20Sopenharmony_ci	nvmem_cell_put(cell);
8228c2ecf20Sopenharmony_ci	if (IS_ERR(ret)) {
8238c2ecf20Sopenharmony_ci		dev_err(dev, "can't read cell %s\n", cname);
8248c2ecf20Sopenharmony_ci		return PTR_ERR(ret);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
8288c2ecf20Sopenharmony_ci		*data |= ret[i] << (8 * i);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	kfree(ret);
8318c2ecf20Sopenharmony_ci	dev_dbg(dev, "efuse read(%s) = %x, bytes %zd\n", cname, *data, len);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	return 0;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int
8378c2ecf20Sopenharmony_cicpr_populate_ring_osc_idx(struct cpr_drv *drv)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct fuse_corner *fuse = drv->fuse_corners;
8408c2ecf20Sopenharmony_ci	struct fuse_corner *end = fuse + drv->desc->num_fuse_corners;
8418c2ecf20Sopenharmony_ci	const struct cpr_fuse *fuses = drv->cpr_fuses;
8428c2ecf20Sopenharmony_ci	u32 data;
8438c2ecf20Sopenharmony_ci	int ret;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	for (; fuse < end; fuse++, fuses++) {
8468c2ecf20Sopenharmony_ci		ret = cpr_read_efuse(drv->dev, fuses->ring_osc,
8478c2ecf20Sopenharmony_ci				     &data);
8488c2ecf20Sopenharmony_ci		if (ret)
8498c2ecf20Sopenharmony_ci			return ret;
8508c2ecf20Sopenharmony_ci		fuse->ring_osc_idx = data;
8518c2ecf20Sopenharmony_ci	}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return 0;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_cistatic int cpr_read_fuse_uV(const struct cpr_desc *desc,
8578c2ecf20Sopenharmony_ci			    const struct fuse_corner_data *fdata,
8588c2ecf20Sopenharmony_ci			    const char *init_v_efuse,
8598c2ecf20Sopenharmony_ci			    int step_volt,
8608c2ecf20Sopenharmony_ci			    struct cpr_drv *drv)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	int step_size_uV, steps, uV;
8638c2ecf20Sopenharmony_ci	u32 bits = 0;
8648c2ecf20Sopenharmony_ci	int ret;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	ret = cpr_read_efuse(drv->dev, init_v_efuse, &bits);
8678c2ecf20Sopenharmony_ci	if (ret)
8688c2ecf20Sopenharmony_ci		return ret;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1);
8718c2ecf20Sopenharmony_ci	/* Not two's complement.. instead highest bit is sign bit */
8728c2ecf20Sopenharmony_ci	if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1))
8738c2ecf20Sopenharmony_ci		steps = -steps;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	step_size_uV = desc->cpr_fuses.init_voltage_step;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	uV = fdata->ref_uV + steps * step_size_uV;
8788c2ecf20Sopenharmony_ci	return DIV_ROUND_UP(uV, step_volt) * step_volt;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int cpr_fuse_corner_init(struct cpr_drv *drv)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
8848c2ecf20Sopenharmony_ci	const struct cpr_fuse *fuses = drv->cpr_fuses;
8858c2ecf20Sopenharmony_ci	const struct acc_desc *acc_desc = drv->acc_desc;
8868c2ecf20Sopenharmony_ci	int i;
8878c2ecf20Sopenharmony_ci	unsigned int step_volt;
8888c2ecf20Sopenharmony_ci	struct fuse_corner_data *fdata;
8898c2ecf20Sopenharmony_ci	struct fuse_corner *fuse, *end;
8908c2ecf20Sopenharmony_ci	int uV;
8918c2ecf20Sopenharmony_ci	const struct reg_sequence *accs;
8928c2ecf20Sopenharmony_ci	int ret;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	accs = acc_desc->settings;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	step_volt = regulator_get_linear_step(drv->vdd_apc);
8978c2ecf20Sopenharmony_ci	if (!step_volt)
8988c2ecf20Sopenharmony_ci		return -EINVAL;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* Populate fuse_corner members */
9018c2ecf20Sopenharmony_ci	fuse = drv->fuse_corners;
9028c2ecf20Sopenharmony_ci	end = &fuse[desc->num_fuse_corners - 1];
9038c2ecf20Sopenharmony_ci	fdata = desc->cpr_fuses.fuse_corner_data;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) {
9068c2ecf20Sopenharmony_ci		/*
9078c2ecf20Sopenharmony_ci		 * Update SoC voltages: platforms might choose a different
9088c2ecf20Sopenharmony_ci		 * regulators than the one used to characterize the algorithms
9098c2ecf20Sopenharmony_ci		 * (ie, init_voltage_step).
9108c2ecf20Sopenharmony_ci		 */
9118c2ecf20Sopenharmony_ci		fdata->min_uV = roundup(fdata->min_uV, step_volt);
9128c2ecf20Sopenharmony_ci		fdata->max_uV = roundup(fdata->max_uV, step_volt);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci		/* Populate uV */
9158c2ecf20Sopenharmony_ci		uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage,
9168c2ecf20Sopenharmony_ci				      step_volt, drv);
9178c2ecf20Sopenharmony_ci		if (uV < 0)
9188c2ecf20Sopenharmony_ci			return uV;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		fuse->min_uV = fdata->min_uV;
9218c2ecf20Sopenharmony_ci		fuse->max_uV = fdata->max_uV;
9228c2ecf20Sopenharmony_ci		fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		if (fuse == end) {
9258c2ecf20Sopenharmony_ci			/*
9268c2ecf20Sopenharmony_ci			 * Allow the highest fuse corner's PVS voltage to
9278c2ecf20Sopenharmony_ci			 * define the ceiling voltage for that corner in order
9288c2ecf20Sopenharmony_ci			 * to support SoC's in which variable ceiling values
9298c2ecf20Sopenharmony_ci			 * are required.
9308c2ecf20Sopenharmony_ci			 */
9318c2ecf20Sopenharmony_ci			end->max_uV = max(end->max_uV, end->uV);
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci		/* Populate target quotient by scaling */
9358c2ecf20Sopenharmony_ci		ret = cpr_read_efuse(drv->dev, fuses->quotient, &fuse->quot);
9368c2ecf20Sopenharmony_ci		if (ret)
9378c2ecf20Sopenharmony_ci			return ret;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		fuse->quot *= fdata->quot_scale;
9408c2ecf20Sopenharmony_ci		fuse->quot += fdata->quot_offset;
9418c2ecf20Sopenharmony_ci		fuse->quot += fdata->quot_adjust;
9428c2ecf20Sopenharmony_ci		fuse->step_quot = desc->step_quot[fuse->ring_osc_idx];
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		/* Populate acc settings */
9458c2ecf20Sopenharmony_ci		fuse->accs = accs;
9468c2ecf20Sopenharmony_ci		fuse->num_accs = acc_desc->num_regs_per_fuse;
9478c2ecf20Sopenharmony_ci		accs += acc_desc->num_regs_per_fuse;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/*
9518c2ecf20Sopenharmony_ci	 * Restrict all fuse corner PVS voltages based upon per corner
9528c2ecf20Sopenharmony_ci	 * ceiling and floor voltages.
9538c2ecf20Sopenharmony_ci	 */
9548c2ecf20Sopenharmony_ci	for (fuse = drv->fuse_corners, i = 0; fuse <= end; fuse++, i++) {
9558c2ecf20Sopenharmony_ci		if (fuse->uV > fuse->max_uV)
9568c2ecf20Sopenharmony_ci			fuse->uV = fuse->max_uV;
9578c2ecf20Sopenharmony_ci		else if (fuse->uV < fuse->min_uV)
9588c2ecf20Sopenharmony_ci			fuse->uV = fuse->min_uV;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci		ret = regulator_is_supported_voltage(drv->vdd_apc,
9618c2ecf20Sopenharmony_ci						     fuse->min_uV,
9628c2ecf20Sopenharmony_ci						     fuse->min_uV);
9638c2ecf20Sopenharmony_ci		if (!ret) {
9648c2ecf20Sopenharmony_ci			dev_err(drv->dev,
9658c2ecf20Sopenharmony_ci				"min uV: %d (fuse corner: %d) not supported by regulator\n",
9668c2ecf20Sopenharmony_ci				fuse->min_uV, i);
9678c2ecf20Sopenharmony_ci			return -EINVAL;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci		ret = regulator_is_supported_voltage(drv->vdd_apc,
9718c2ecf20Sopenharmony_ci						     fuse->max_uV,
9728c2ecf20Sopenharmony_ci						     fuse->max_uV);
9738c2ecf20Sopenharmony_ci		if (!ret) {
9748c2ecf20Sopenharmony_ci			dev_err(drv->dev,
9758c2ecf20Sopenharmony_ci				"max uV: %d (fuse corner: %d) not supported by regulator\n",
9768c2ecf20Sopenharmony_ci				fuse->max_uV, i);
9778c2ecf20Sopenharmony_ci			return -EINVAL;
9788c2ecf20Sopenharmony_ci		}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		dev_dbg(drv->dev,
9818c2ecf20Sopenharmony_ci			"fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n",
9828c2ecf20Sopenharmony_ci			i, fuse->min_uV, fuse->uV, fuse->max_uV,
9838c2ecf20Sopenharmony_ci			fuse->ring_osc_idx, fuse->quot, fuse->step_quot);
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	return 0;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic int cpr_calculate_scaling(const char *quot_offset,
9908c2ecf20Sopenharmony_ci				 struct cpr_drv *drv,
9918c2ecf20Sopenharmony_ci				 const struct fuse_corner_data *fdata,
9928c2ecf20Sopenharmony_ci				 const struct corner *corner)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	u32 quot_diff = 0;
9958c2ecf20Sopenharmony_ci	unsigned long freq_diff;
9968c2ecf20Sopenharmony_ci	int scaling;
9978c2ecf20Sopenharmony_ci	const struct fuse_corner *fuse, *prev_fuse;
9988c2ecf20Sopenharmony_ci	int ret;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	fuse = corner->fuse_corner;
10018c2ecf20Sopenharmony_ci	prev_fuse = fuse - 1;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	if (quot_offset) {
10048c2ecf20Sopenharmony_ci		ret = cpr_read_efuse(drv->dev, quot_offset, &quot_diff);
10058c2ecf20Sopenharmony_ci		if (ret)
10068c2ecf20Sopenharmony_ci			return ret;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci		quot_diff *= fdata->quot_offset_scale;
10098c2ecf20Sopenharmony_ci		quot_diff += fdata->quot_offset_adjust;
10108c2ecf20Sopenharmony_ci	} else {
10118c2ecf20Sopenharmony_ci		quot_diff = fuse->quot - prev_fuse->quot;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	freq_diff = fuse->max_freq - prev_fuse->max_freq;
10158c2ecf20Sopenharmony_ci	freq_diff /= 1000000; /* Convert to MHz */
10168c2ecf20Sopenharmony_ci	scaling = 1000 * quot_diff / freq_diff;
10178c2ecf20Sopenharmony_ci	return min(scaling, fdata->max_quot_scale);
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic int cpr_interpolate(const struct corner *corner, int step_volt,
10218c2ecf20Sopenharmony_ci			   const struct fuse_corner_data *fdata)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	unsigned long f_high, f_low, f_diff;
10248c2ecf20Sopenharmony_ci	int uV_high, uV_low, uV;
10258c2ecf20Sopenharmony_ci	u64 temp, temp_limit;
10268c2ecf20Sopenharmony_ci	const struct fuse_corner *fuse, *prev_fuse;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	fuse = corner->fuse_corner;
10298c2ecf20Sopenharmony_ci	prev_fuse = fuse - 1;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	f_high = fuse->max_freq;
10328c2ecf20Sopenharmony_ci	f_low = prev_fuse->max_freq;
10338c2ecf20Sopenharmony_ci	uV_high = fuse->uV;
10348c2ecf20Sopenharmony_ci	uV_low = prev_fuse->uV;
10358c2ecf20Sopenharmony_ci	f_diff = fuse->max_freq - corner->freq;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	/*
10388c2ecf20Sopenharmony_ci	 * Don't interpolate in the wrong direction. This could happen
10398c2ecf20Sopenharmony_ci	 * if the adjusted fuse voltage overlaps with the previous fuse's
10408c2ecf20Sopenharmony_ci	 * adjusted voltage.
10418c2ecf20Sopenharmony_ci	 */
10428c2ecf20Sopenharmony_ci	if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq)
10438c2ecf20Sopenharmony_ci		return corner->uV;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	temp = f_diff * (uV_high - uV_low);
10468c2ecf20Sopenharmony_ci	temp = div64_ul(temp, f_high - f_low);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/*
10498c2ecf20Sopenharmony_ci	 * max_volt_scale has units of uV/MHz while freq values
10508c2ecf20Sopenharmony_ci	 * have units of Hz.  Divide by 1000000 to convert to.
10518c2ecf20Sopenharmony_ci	 */
10528c2ecf20Sopenharmony_ci	temp_limit = f_diff * fdata->max_volt_scale;
10538c2ecf20Sopenharmony_ci	do_div(temp_limit, 1000000);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	uV = uV_high - min(temp, temp_limit);
10568c2ecf20Sopenharmony_ci	return roundup(uV, step_volt);
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	struct device_node *np;
10628c2ecf20Sopenharmony_ci	unsigned int fuse_corner = 0;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	np = dev_pm_opp_get_of_node(opp);
10658c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner))
10668c2ecf20Sopenharmony_ci		pr_err("%s: missing 'qcom,opp-fuse-level' property\n",
10678c2ecf20Sopenharmony_ci		       __func__);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	of_node_put(np);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	return fuse_corner;
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref,
10758c2ecf20Sopenharmony_ci					    struct device *cpu_dev)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	u64 rate = 0;
10788c2ecf20Sopenharmony_ci	struct device_node *ref_np;
10798c2ecf20Sopenharmony_ci	struct device_node *desc_np;
10808c2ecf20Sopenharmony_ci	struct device_node *child_np = NULL;
10818c2ecf20Sopenharmony_ci	struct device_node *child_req_np = NULL;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
10848c2ecf20Sopenharmony_ci	if (!desc_np)
10858c2ecf20Sopenharmony_ci		return 0;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	ref_np = dev_pm_opp_get_of_node(ref);
10888c2ecf20Sopenharmony_ci	if (!ref_np)
10898c2ecf20Sopenharmony_ci		goto out_ref;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	do {
10928c2ecf20Sopenharmony_ci		of_node_put(child_req_np);
10938c2ecf20Sopenharmony_ci		child_np = of_get_next_available_child(desc_np, child_np);
10948c2ecf20Sopenharmony_ci		child_req_np = of_parse_phandle(child_np, "required-opps", 0);
10958c2ecf20Sopenharmony_ci	} while (child_np && child_req_np != ref_np);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (child_np && child_req_np == ref_np)
10988c2ecf20Sopenharmony_ci		of_property_read_u64(child_np, "opp-hz", &rate);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	of_node_put(child_req_np);
11018c2ecf20Sopenharmony_ci	of_node_put(child_np);
11028c2ecf20Sopenharmony_ci	of_node_put(ref_np);
11038c2ecf20Sopenharmony_ciout_ref:
11048c2ecf20Sopenharmony_ci	of_node_put(desc_np);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	return (unsigned long) rate;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int cpr_corner_init(struct cpr_drv *drv)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
11128c2ecf20Sopenharmony_ci	const struct cpr_fuse *fuses = drv->cpr_fuses;
11138c2ecf20Sopenharmony_ci	int i, level, scaling = 0;
11148c2ecf20Sopenharmony_ci	unsigned int fnum, fc;
11158c2ecf20Sopenharmony_ci	const char *quot_offset;
11168c2ecf20Sopenharmony_ci	struct fuse_corner *fuse, *prev_fuse;
11178c2ecf20Sopenharmony_ci	struct corner *corner, *end;
11188c2ecf20Sopenharmony_ci	struct corner_data *cdata;
11198c2ecf20Sopenharmony_ci	const struct fuse_corner_data *fdata;
11208c2ecf20Sopenharmony_ci	bool apply_scaling;
11218c2ecf20Sopenharmony_ci	unsigned long freq_diff, freq_diff_mhz;
11228c2ecf20Sopenharmony_ci	unsigned long freq;
11238c2ecf20Sopenharmony_ci	int step_volt = regulator_get_linear_step(drv->vdd_apc);
11248c2ecf20Sopenharmony_ci	struct dev_pm_opp *opp;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	if (!step_volt)
11278c2ecf20Sopenharmony_ci		return -EINVAL;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	corner = drv->corners;
11308c2ecf20Sopenharmony_ci	end = &corner[drv->num_corners - 1];
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	cdata = devm_kcalloc(drv->dev, drv->num_corners,
11338c2ecf20Sopenharmony_ci			     sizeof(struct corner_data),
11348c2ecf20Sopenharmony_ci			     GFP_KERNEL);
11358c2ecf20Sopenharmony_ci	if (!cdata)
11368c2ecf20Sopenharmony_ci		return -ENOMEM;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	/*
11398c2ecf20Sopenharmony_ci	 * Store maximum frequency for each fuse corner based on the frequency
11408c2ecf20Sopenharmony_ci	 * plan
11418c2ecf20Sopenharmony_ci	 */
11428c2ecf20Sopenharmony_ci	for (level = 1; level <= drv->num_corners; level++) {
11438c2ecf20Sopenharmony_ci		opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level);
11448c2ecf20Sopenharmony_ci		if (IS_ERR(opp))
11458c2ecf20Sopenharmony_ci			return -EINVAL;
11468c2ecf20Sopenharmony_ci		fc = cpr_get_fuse_corner(opp);
11478c2ecf20Sopenharmony_ci		if (!fc) {
11488c2ecf20Sopenharmony_ci			dev_pm_opp_put(opp);
11498c2ecf20Sopenharmony_ci			return -EINVAL;
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci		fnum = fc - 1;
11528c2ecf20Sopenharmony_ci		freq = cpr_get_opp_hz_for_req(opp, drv->attached_cpu_dev);
11538c2ecf20Sopenharmony_ci		if (!freq) {
11548c2ecf20Sopenharmony_ci			dev_pm_opp_put(opp);
11558c2ecf20Sopenharmony_ci			return -EINVAL;
11568c2ecf20Sopenharmony_ci		}
11578c2ecf20Sopenharmony_ci		cdata[level - 1].fuse_corner = fnum;
11588c2ecf20Sopenharmony_ci		cdata[level - 1].freq = freq;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci		fuse = &drv->fuse_corners[fnum];
11618c2ecf20Sopenharmony_ci		dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n",
11628c2ecf20Sopenharmony_ci			freq, dev_pm_opp_get_level(opp) - 1, fnum);
11638c2ecf20Sopenharmony_ci		if (freq > fuse->max_freq)
11648c2ecf20Sopenharmony_ci			fuse->max_freq = freq;
11658c2ecf20Sopenharmony_ci		dev_pm_opp_put(opp);
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/*
11698c2ecf20Sopenharmony_ci	 * Get the quotient adjustment scaling factor, according to:
11708c2ecf20Sopenharmony_ci	 *
11718c2ecf20Sopenharmony_ci	 * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1))
11728c2ecf20Sopenharmony_ci	 *		/ (freq(corner_N) - freq(corner_N-1)), max_factor)
11738c2ecf20Sopenharmony_ci	 *
11748c2ecf20Sopenharmony_ci	 * QUOT(corner_N):	quotient read from fuse for fuse corner N
11758c2ecf20Sopenharmony_ci	 * QUOT(corner_N-1):	quotient read from fuse for fuse corner (N - 1)
11768c2ecf20Sopenharmony_ci	 * freq(corner_N):	max frequency in MHz supported by fuse corner N
11778c2ecf20Sopenharmony_ci	 * freq(corner_N-1):	max frequency in MHz supported by fuse corner
11788c2ecf20Sopenharmony_ci	 *			 (N - 1)
11798c2ecf20Sopenharmony_ci	 *
11808c2ecf20Sopenharmony_ci	 * Then walk through the corners mapped to each fuse corner
11818c2ecf20Sopenharmony_ci	 * and calculate the quotient adjustment for each one using the
11828c2ecf20Sopenharmony_ci	 * following formula:
11838c2ecf20Sopenharmony_ci	 *
11848c2ecf20Sopenharmony_ci	 * quot_adjust = (freq_max - freq_corner) * scaling / 1000
11858c2ecf20Sopenharmony_ci	 *
11868c2ecf20Sopenharmony_ci	 * freq_max: max frequency in MHz supported by the fuse corner
11878c2ecf20Sopenharmony_ci	 * freq_corner: frequency in MHz corresponding to the corner
11888c2ecf20Sopenharmony_ci	 * scaling: calculated from above equation
11898c2ecf20Sopenharmony_ci	 *
11908c2ecf20Sopenharmony_ci	 *
11918c2ecf20Sopenharmony_ci	 *     +                           +
11928c2ecf20Sopenharmony_ci	 *     |                         v |
11938c2ecf20Sopenharmony_ci	 *   q |           f c           o |           f c
11948c2ecf20Sopenharmony_ci	 *   u |         c               l |         c
11958c2ecf20Sopenharmony_ci	 *   o |       f                 t |       f
11968c2ecf20Sopenharmony_ci	 *   t |     c                   a |     c
11978c2ecf20Sopenharmony_ci	 *     | c f                     g | c f
11988c2ecf20Sopenharmony_ci	 *     |                         e |
11998c2ecf20Sopenharmony_ci	 *     +---------------            +----------------
12008c2ecf20Sopenharmony_ci	 *       0 1 2 3 4 5 6               0 1 2 3 4 5 6
12018c2ecf20Sopenharmony_ci	 *          corner                      corner
12028c2ecf20Sopenharmony_ci	 *
12038c2ecf20Sopenharmony_ci	 *    c = corner
12048c2ecf20Sopenharmony_ci	 *    f = fuse corner
12058c2ecf20Sopenharmony_ci	 *
12068c2ecf20Sopenharmony_ci	 */
12078c2ecf20Sopenharmony_ci	for (apply_scaling = false, i = 0; corner <= end; corner++, i++) {
12088c2ecf20Sopenharmony_ci		fnum = cdata[i].fuse_corner;
12098c2ecf20Sopenharmony_ci		fdata = &desc->cpr_fuses.fuse_corner_data[fnum];
12108c2ecf20Sopenharmony_ci		quot_offset = fuses[fnum].quotient_offset;
12118c2ecf20Sopenharmony_ci		fuse = &drv->fuse_corners[fnum];
12128c2ecf20Sopenharmony_ci		if (fnum)
12138c2ecf20Sopenharmony_ci			prev_fuse = &drv->fuse_corners[fnum - 1];
12148c2ecf20Sopenharmony_ci		else
12158c2ecf20Sopenharmony_ci			prev_fuse = NULL;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		corner->fuse_corner = fuse;
12188c2ecf20Sopenharmony_ci		corner->freq = cdata[i].freq;
12198c2ecf20Sopenharmony_ci		corner->uV = fuse->uV;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci		if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) {
12228c2ecf20Sopenharmony_ci			scaling = cpr_calculate_scaling(quot_offset, drv,
12238c2ecf20Sopenharmony_ci							fdata, corner);
12248c2ecf20Sopenharmony_ci			if (scaling < 0)
12258c2ecf20Sopenharmony_ci				return scaling;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci			apply_scaling = true;
12288c2ecf20Sopenharmony_ci		} else if (corner->freq == fuse->max_freq) {
12298c2ecf20Sopenharmony_ci			/* This is a fuse corner; don't scale anything */
12308c2ecf20Sopenharmony_ci			apply_scaling = false;
12318c2ecf20Sopenharmony_ci		}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci		if (apply_scaling) {
12348c2ecf20Sopenharmony_ci			freq_diff = fuse->max_freq - corner->freq;
12358c2ecf20Sopenharmony_ci			freq_diff_mhz = freq_diff / 1000000;
12368c2ecf20Sopenharmony_ci			corner->quot_adjust = scaling * freq_diff_mhz / 1000;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci			corner->uV = cpr_interpolate(corner, step_volt, fdata);
12398c2ecf20Sopenharmony_ci		}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci		corner->max_uV = fuse->max_uV;
12428c2ecf20Sopenharmony_ci		corner->min_uV = fuse->min_uV;
12438c2ecf20Sopenharmony_ci		corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV);
12448c2ecf20Sopenharmony_ci		corner->last_uV = corner->uV;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci		/* Reduce the ceiling voltage if needed */
12478c2ecf20Sopenharmony_ci		if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV)
12488c2ecf20Sopenharmony_ci			corner->max_uV = corner->uV;
12498c2ecf20Sopenharmony_ci		else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV)
12508c2ecf20Sopenharmony_ci			corner->max_uV = max(corner->min_uV, fuse->uV);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci		dev_dbg(drv->dev, "corner %d: [%d %d %d] quot %d\n", i,
12538c2ecf20Sopenharmony_ci			corner->min_uV, corner->uV, corner->max_uV,
12548c2ecf20Sopenharmony_ci			fuse->quot - corner->quot_adjust);
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	return 0;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic const struct cpr_fuse *cpr_get_fuses(struct cpr_drv *drv)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
12638c2ecf20Sopenharmony_ci	struct cpr_fuse *fuses;
12648c2ecf20Sopenharmony_ci	int i;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	fuses = devm_kcalloc(drv->dev, desc->num_fuse_corners,
12678c2ecf20Sopenharmony_ci			     sizeof(struct cpr_fuse),
12688c2ecf20Sopenharmony_ci			     GFP_KERNEL);
12698c2ecf20Sopenharmony_ci	if (!fuses)
12708c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	for (i = 0; i < desc->num_fuse_corners; i++) {
12738c2ecf20Sopenharmony_ci		char tbuf[32];
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci		snprintf(tbuf, 32, "cpr_ring_osc%d", i + 1);
12768c2ecf20Sopenharmony_ci		fuses[i].ring_osc = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL);
12778c2ecf20Sopenharmony_ci		if (!fuses[i].ring_osc)
12788c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci		snprintf(tbuf, 32, "cpr_init_voltage%d", i + 1);
12818c2ecf20Sopenharmony_ci		fuses[i].init_voltage = devm_kstrdup(drv->dev, tbuf,
12828c2ecf20Sopenharmony_ci						     GFP_KERNEL);
12838c2ecf20Sopenharmony_ci		if (!fuses[i].init_voltage)
12848c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		snprintf(tbuf, 32, "cpr_quotient%d", i + 1);
12878c2ecf20Sopenharmony_ci		fuses[i].quotient = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL);
12888c2ecf20Sopenharmony_ci		if (!fuses[i].quotient)
12898c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci		snprintf(tbuf, 32, "cpr_quotient_offset%d", i + 1);
12928c2ecf20Sopenharmony_ci		fuses[i].quotient_offset = devm_kstrdup(drv->dev, tbuf,
12938c2ecf20Sopenharmony_ci							GFP_KERNEL);
12948c2ecf20Sopenharmony_ci		if (!fuses[i].quotient_offset)
12958c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
12968c2ecf20Sopenharmony_ci	}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	return fuses;
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_cistatic void cpr_set_loop_allowed(struct cpr_drv *drv)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	drv->loop_disabled = false;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic int cpr_init_parameters(struct cpr_drv *drv)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	const struct cpr_desc *desc = drv->desc;
13098c2ecf20Sopenharmony_ci	struct clk *clk;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	clk = clk_get(drv->dev, "ref");
13128c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
13138c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	drv->ref_clk_khz = clk_get_rate(clk) / 1000;
13168c2ecf20Sopenharmony_ci	clk_put(clk);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	if (desc->timer_cons_up > RBIF_TIMER_ADJ_CONS_UP_MASK ||
13198c2ecf20Sopenharmony_ci	    desc->timer_cons_down > RBIF_TIMER_ADJ_CONS_DOWN_MASK ||
13208c2ecf20Sopenharmony_ci	    desc->up_threshold > RBCPR_CTL_UP_THRESHOLD_MASK ||
13218c2ecf20Sopenharmony_ci	    desc->down_threshold > RBCPR_CTL_DN_THRESHOLD_MASK ||
13228c2ecf20Sopenharmony_ci	    desc->idle_clocks > RBCPR_STEP_QUOT_IDLE_CLK_MASK ||
13238c2ecf20Sopenharmony_ci	    desc->clamp_timer_interval > RBIF_TIMER_ADJ_CLAMP_INT_MASK)
13248c2ecf20Sopenharmony_ci		return -EINVAL;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "up threshold = %u, down threshold = %u\n",
13278c2ecf20Sopenharmony_ci		desc->up_threshold, desc->down_threshold);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	return 0;
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic int cpr_find_initial_corner(struct cpr_drv *drv)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	unsigned long rate;
13358c2ecf20Sopenharmony_ci	const struct corner *end;
13368c2ecf20Sopenharmony_ci	struct corner *iter;
13378c2ecf20Sopenharmony_ci	unsigned int i = 0;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	if (!drv->cpu_clk) {
13408c2ecf20Sopenharmony_ci		dev_err(drv->dev, "cannot get rate from NULL clk\n");
13418c2ecf20Sopenharmony_ci		return -EINVAL;
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	end = &drv->corners[drv->num_corners - 1];
13458c2ecf20Sopenharmony_ci	rate = clk_get_rate(drv->cpu_clk);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	/*
13488c2ecf20Sopenharmony_ci	 * Some bootloaders set a CPU clock frequency that is not defined
13498c2ecf20Sopenharmony_ci	 * in the OPP table. When running at an unlisted frequency,
13508c2ecf20Sopenharmony_ci	 * cpufreq_online() will change to the OPP which has the lowest
13518c2ecf20Sopenharmony_ci	 * frequency, at or above the unlisted frequency.
13528c2ecf20Sopenharmony_ci	 * Since cpufreq_online() always "rounds up" in the case of an
13538c2ecf20Sopenharmony_ci	 * unlisted frequency, this function always "rounds down" in case
13548c2ecf20Sopenharmony_ci	 * of an unlisted frequency. That way, when cpufreq_online()
13558c2ecf20Sopenharmony_ci	 * triggers the first ever call to cpr_set_performance_state(),
13568c2ecf20Sopenharmony_ci	 * it will correctly determine the direction as UP.
13578c2ecf20Sopenharmony_ci	 */
13588c2ecf20Sopenharmony_ci	for (iter = drv->corners; iter <= end; iter++) {
13598c2ecf20Sopenharmony_ci		if (iter->freq > rate)
13608c2ecf20Sopenharmony_ci			break;
13618c2ecf20Sopenharmony_ci		i++;
13628c2ecf20Sopenharmony_ci		if (iter->freq == rate) {
13638c2ecf20Sopenharmony_ci			drv->corner = iter;
13648c2ecf20Sopenharmony_ci			break;
13658c2ecf20Sopenharmony_ci		}
13668c2ecf20Sopenharmony_ci		if (iter->freq < rate)
13678c2ecf20Sopenharmony_ci			drv->corner = iter;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (!drv->corner) {
13718c2ecf20Sopenharmony_ci		dev_err(drv->dev, "boot up corner not found\n");
13728c2ecf20Sopenharmony_ci		return -EINVAL;
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "boot up perf state: %u\n", i);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	return 0;
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic const struct cpr_desc qcs404_cpr_desc = {
13818c2ecf20Sopenharmony_ci	.num_fuse_corners = 3,
13828c2ecf20Sopenharmony_ci	.min_diff_quot = CPR_FUSE_MIN_QUOT_DIFF,
13838c2ecf20Sopenharmony_ci	.step_quot = (int []){ 25, 25, 25, },
13848c2ecf20Sopenharmony_ci	.timer_delay_us = 5000,
13858c2ecf20Sopenharmony_ci	.timer_cons_up = 0,
13868c2ecf20Sopenharmony_ci	.timer_cons_down = 2,
13878c2ecf20Sopenharmony_ci	.up_threshold = 1,
13888c2ecf20Sopenharmony_ci	.down_threshold = 3,
13898c2ecf20Sopenharmony_ci	.idle_clocks = 15,
13908c2ecf20Sopenharmony_ci	.gcnt_us = 1,
13918c2ecf20Sopenharmony_ci	.vdd_apc_step_up_limit = 1,
13928c2ecf20Sopenharmony_ci	.vdd_apc_step_down_limit = 1,
13938c2ecf20Sopenharmony_ci	.cpr_fuses = {
13948c2ecf20Sopenharmony_ci		.init_voltage_step = 8000,
13958c2ecf20Sopenharmony_ci		.init_voltage_width = 6,
13968c2ecf20Sopenharmony_ci		.fuse_corner_data = (struct fuse_corner_data[]){
13978c2ecf20Sopenharmony_ci			/* fuse corner 0 */
13988c2ecf20Sopenharmony_ci			{
13998c2ecf20Sopenharmony_ci				.ref_uV = 1224000,
14008c2ecf20Sopenharmony_ci				.max_uV = 1224000,
14018c2ecf20Sopenharmony_ci				.min_uV = 1048000,
14028c2ecf20Sopenharmony_ci				.max_volt_scale = 0,
14038c2ecf20Sopenharmony_ci				.max_quot_scale = 0,
14048c2ecf20Sopenharmony_ci				.quot_offset = 0,
14058c2ecf20Sopenharmony_ci				.quot_scale = 1,
14068c2ecf20Sopenharmony_ci				.quot_adjust = 0,
14078c2ecf20Sopenharmony_ci				.quot_offset_scale = 5,
14088c2ecf20Sopenharmony_ci				.quot_offset_adjust = 0,
14098c2ecf20Sopenharmony_ci			},
14108c2ecf20Sopenharmony_ci			/* fuse corner 1 */
14118c2ecf20Sopenharmony_ci			{
14128c2ecf20Sopenharmony_ci				.ref_uV = 1288000,
14138c2ecf20Sopenharmony_ci				.max_uV = 1288000,
14148c2ecf20Sopenharmony_ci				.min_uV = 1048000,
14158c2ecf20Sopenharmony_ci				.max_volt_scale = 2000,
14168c2ecf20Sopenharmony_ci				.max_quot_scale = 1400,
14178c2ecf20Sopenharmony_ci				.quot_offset = 0,
14188c2ecf20Sopenharmony_ci				.quot_scale = 1,
14198c2ecf20Sopenharmony_ci				.quot_adjust = -20,
14208c2ecf20Sopenharmony_ci				.quot_offset_scale = 5,
14218c2ecf20Sopenharmony_ci				.quot_offset_adjust = 0,
14228c2ecf20Sopenharmony_ci			},
14238c2ecf20Sopenharmony_ci			/* fuse corner 2 */
14248c2ecf20Sopenharmony_ci			{
14258c2ecf20Sopenharmony_ci				.ref_uV = 1352000,
14268c2ecf20Sopenharmony_ci				.max_uV = 1384000,
14278c2ecf20Sopenharmony_ci				.min_uV = 1088000,
14288c2ecf20Sopenharmony_ci				.max_volt_scale = 2000,
14298c2ecf20Sopenharmony_ci				.max_quot_scale = 1400,
14308c2ecf20Sopenharmony_ci				.quot_offset = 0,
14318c2ecf20Sopenharmony_ci				.quot_scale = 1,
14328c2ecf20Sopenharmony_ci				.quot_adjust = 0,
14338c2ecf20Sopenharmony_ci				.quot_offset_scale = 5,
14348c2ecf20Sopenharmony_ci				.quot_offset_adjust = 0,
14358c2ecf20Sopenharmony_ci			},
14368c2ecf20Sopenharmony_ci		},
14378c2ecf20Sopenharmony_ci	},
14388c2ecf20Sopenharmony_ci};
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic const struct acc_desc qcs404_acc_desc = {
14418c2ecf20Sopenharmony_ci	.settings = (struct reg_sequence[]){
14428c2ecf20Sopenharmony_ci		{ 0xb120, 0x1041040 },
14438c2ecf20Sopenharmony_ci		{ 0xb124, 0x41 },
14448c2ecf20Sopenharmony_ci		{ 0xb120, 0x0 },
14458c2ecf20Sopenharmony_ci		{ 0xb124, 0x0 },
14468c2ecf20Sopenharmony_ci		{ 0xb120, 0x0 },
14478c2ecf20Sopenharmony_ci		{ 0xb124, 0x0 },
14488c2ecf20Sopenharmony_ci	},
14498c2ecf20Sopenharmony_ci	.config = (struct reg_sequence[]){
14508c2ecf20Sopenharmony_ci		{ 0xb138, 0xff },
14518c2ecf20Sopenharmony_ci		{ 0xb130, 0x5555 },
14528c2ecf20Sopenharmony_ci	},
14538c2ecf20Sopenharmony_ci	.num_regs_per_fuse = 2,
14548c2ecf20Sopenharmony_ci};
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic const struct cpr_acc_desc qcs404_cpr_acc_desc = {
14578c2ecf20Sopenharmony_ci	.cpr_desc = &qcs404_cpr_desc,
14588c2ecf20Sopenharmony_ci	.acc_desc = &qcs404_acc_desc,
14598c2ecf20Sopenharmony_ci};
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic unsigned int cpr_get_performance_state(struct generic_pm_domain *genpd,
14628c2ecf20Sopenharmony_ci					      struct dev_pm_opp *opp)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	return dev_pm_opp_get_level(opp);
14658c2ecf20Sopenharmony_ci}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_cistatic int cpr_power_off(struct generic_pm_domain *domain)
14688c2ecf20Sopenharmony_ci{
14698c2ecf20Sopenharmony_ci	struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	return cpr_disable(drv);
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic int cpr_power_on(struct generic_pm_domain *domain)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	return cpr_enable(drv);
14798c2ecf20Sopenharmony_ci}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cistatic int cpr_pd_attach_dev(struct generic_pm_domain *domain,
14828c2ecf20Sopenharmony_ci			     struct device *dev)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd);
14858c2ecf20Sopenharmony_ci	const struct acc_desc *acc_desc = drv->acc_desc;
14868c2ecf20Sopenharmony_ci	int ret = 0;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	mutex_lock(&drv->lock);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev));
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	/*
14938c2ecf20Sopenharmony_ci	 * This driver only supports scaling voltage for a CPU cluster
14948c2ecf20Sopenharmony_ci	 * where all CPUs in the cluster share a single regulator.
14958c2ecf20Sopenharmony_ci	 * Therefore, save the struct device pointer only for the first
14968c2ecf20Sopenharmony_ci	 * CPU device that gets attached. There is no need to do any
14978c2ecf20Sopenharmony_ci	 * additional initialization when further CPUs get attached.
14988c2ecf20Sopenharmony_ci	 */
14998c2ecf20Sopenharmony_ci	if (drv->attached_cpu_dev)
15008c2ecf20Sopenharmony_ci		goto unlock;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	/*
15038c2ecf20Sopenharmony_ci	 * cpr_scale_voltage() requires the direction (if we are changing
15048c2ecf20Sopenharmony_ci	 * to a higher or lower OPP). The first time
15058c2ecf20Sopenharmony_ci	 * cpr_set_performance_state() is called, there is no previous
15068c2ecf20Sopenharmony_ci	 * performance state defined. Therefore, we call
15078c2ecf20Sopenharmony_ci	 * cpr_find_initial_corner() that gets the CPU clock frequency
15088c2ecf20Sopenharmony_ci	 * set by the bootloader, so that we can determine the direction
15098c2ecf20Sopenharmony_ci	 * the first time cpr_set_performance_state() is called.
15108c2ecf20Sopenharmony_ci	 */
15118c2ecf20Sopenharmony_ci	drv->cpu_clk = devm_clk_get(dev, NULL);
15128c2ecf20Sopenharmony_ci	if (IS_ERR(drv->cpu_clk)) {
15138c2ecf20Sopenharmony_ci		ret = PTR_ERR(drv->cpu_clk);
15148c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
15158c2ecf20Sopenharmony_ci			dev_err(drv->dev, "could not get cpu clk: %d\n", ret);
15168c2ecf20Sopenharmony_ci		goto unlock;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci	drv->attached_cpu_dev = dev;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	dev_dbg(drv->dev, "using cpu clk from: %s\n",
15218c2ecf20Sopenharmony_ci		dev_name(drv->attached_cpu_dev));
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	/*
15248c2ecf20Sopenharmony_ci	 * Everything related to (virtual) corners has to be initialized
15258c2ecf20Sopenharmony_ci	 * here, when attaching to the power domain, since we need to know
15268c2ecf20Sopenharmony_ci	 * the maximum frequency for each fuse corner, and this is only
15278c2ecf20Sopenharmony_ci	 * available after the cpufreq driver has attached to us.
15288c2ecf20Sopenharmony_ci	 * The reason for this is that we need to know the highest
15298c2ecf20Sopenharmony_ci	 * frequency associated with each fuse corner.
15308c2ecf20Sopenharmony_ci	 */
15318c2ecf20Sopenharmony_ci	ret = dev_pm_opp_get_opp_count(&drv->pd.dev);
15328c2ecf20Sopenharmony_ci	if (ret < 0) {
15338c2ecf20Sopenharmony_ci		dev_err(drv->dev, "could not get OPP count\n");
15348c2ecf20Sopenharmony_ci		goto unlock;
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci	drv->num_corners = ret;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	if (drv->num_corners < 2) {
15398c2ecf20Sopenharmony_ci		dev_err(drv->dev, "need at least 2 OPPs to use CPR\n");
15408c2ecf20Sopenharmony_ci		ret = -EINVAL;
15418c2ecf20Sopenharmony_ci		goto unlock;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	drv->corners = devm_kcalloc(drv->dev, drv->num_corners,
15458c2ecf20Sopenharmony_ci				    sizeof(*drv->corners),
15468c2ecf20Sopenharmony_ci				    GFP_KERNEL);
15478c2ecf20Sopenharmony_ci	if (!drv->corners) {
15488c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15498c2ecf20Sopenharmony_ci		goto unlock;
15508c2ecf20Sopenharmony_ci	}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	ret = cpr_corner_init(drv);
15538c2ecf20Sopenharmony_ci	if (ret)
15548c2ecf20Sopenharmony_ci		goto unlock;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	cpr_set_loop_allowed(drv);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	ret = cpr_init_parameters(drv);
15598c2ecf20Sopenharmony_ci	if (ret)
15608c2ecf20Sopenharmony_ci		goto unlock;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	/* Configure CPR HW but keep it disabled */
15638c2ecf20Sopenharmony_ci	ret = cpr_config(drv);
15648c2ecf20Sopenharmony_ci	if (ret)
15658c2ecf20Sopenharmony_ci		goto unlock;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	ret = cpr_find_initial_corner(drv);
15688c2ecf20Sopenharmony_ci	if (ret)
15698c2ecf20Sopenharmony_ci		goto unlock;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	if (acc_desc->config)
15728c2ecf20Sopenharmony_ci		regmap_multi_reg_write(drv->tcsr, acc_desc->config,
15738c2ecf20Sopenharmony_ci				       acc_desc->num_regs_per_fuse);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	/* Enable ACC if required */
15768c2ecf20Sopenharmony_ci	if (acc_desc->enable_mask)
15778c2ecf20Sopenharmony_ci		regmap_update_bits(drv->tcsr, acc_desc->enable_reg,
15788c2ecf20Sopenharmony_ci				   acc_desc->enable_mask,
15798c2ecf20Sopenharmony_ci				   acc_desc->enable_mask);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	dev_info(drv->dev, "driver initialized with %u OPPs\n",
15828c2ecf20Sopenharmony_ci		 drv->num_corners);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ciunlock:
15858c2ecf20Sopenharmony_ci	mutex_unlock(&drv->lock);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	return ret;
15888c2ecf20Sopenharmony_ci}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_cistatic int cpr_debug_info_show(struct seq_file *s, void *unused)
15918c2ecf20Sopenharmony_ci{
15928c2ecf20Sopenharmony_ci	u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps;
15938c2ecf20Sopenharmony_ci	u32 step_dn, step_up, error, error_lt0, busy;
15948c2ecf20Sopenharmony_ci	struct cpr_drv *drv = s->private;
15958c2ecf20Sopenharmony_ci	struct fuse_corner *fuse_corner;
15968c2ecf20Sopenharmony_ci	struct corner *corner;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	corner = drv->corner;
15998c2ecf20Sopenharmony_ci	fuse_corner = corner->fuse_corner;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	seq_printf(s, "corner, current_volt = %d uV\n",
16028c2ecf20Sopenharmony_ci		       corner->last_uV);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	ro_sel = fuse_corner->ring_osc_idx;
16058c2ecf20Sopenharmony_ci	gcnt = cpr_read(drv, REG_RBCPR_GCNT_TARGET(ro_sel));
16068c2ecf20Sopenharmony_ci	seq_printf(s, "rbcpr_gcnt_target (%u) = %#02X\n", ro_sel, gcnt);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	ctl = cpr_read(drv, REG_RBCPR_CTL);
16098c2ecf20Sopenharmony_ci	seq_printf(s, "rbcpr_ctl = %#02X\n", ctl);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	irq_status = cpr_read(drv, REG_RBIF_IRQ_STATUS);
16128c2ecf20Sopenharmony_ci	seq_printf(s, "rbcpr_irq_status = %#02X\n", irq_status);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	reg = cpr_read(drv, REG_RBCPR_RESULT_0);
16158c2ecf20Sopenharmony_ci	seq_printf(s, "rbcpr_result_0 = %#02X\n", reg);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	step_dn = reg & 0x01;
16188c2ecf20Sopenharmony_ci	step_up = (reg >> RBCPR_RESULT0_STEP_UP_SHIFT) & 0x01;
16198c2ecf20Sopenharmony_ci	seq_printf(s, "  [step_dn = %u", step_dn);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	seq_printf(s, ", step_up = %u", step_up);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	error_steps = (reg >> RBCPR_RESULT0_ERROR_STEPS_SHIFT)
16248c2ecf20Sopenharmony_ci				& RBCPR_RESULT0_ERROR_STEPS_MASK;
16258c2ecf20Sopenharmony_ci	seq_printf(s, ", error_steps = %u", error_steps);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	error = (reg >> RBCPR_RESULT0_ERROR_SHIFT) & RBCPR_RESULT0_ERROR_MASK;
16288c2ecf20Sopenharmony_ci	seq_printf(s, ", error = %u", error);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	error_lt0 = (reg >> RBCPR_RESULT0_ERROR_LT0_SHIFT) & 0x01;
16318c2ecf20Sopenharmony_ci	seq_printf(s, ", error_lt_0 = %u", error_lt0);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	busy = (reg >> RBCPR_RESULT0_BUSY_SHIFT) & 0x01;
16348c2ecf20Sopenharmony_ci	seq_printf(s, ", busy = %u]\n", busy);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	return 0;
16378c2ecf20Sopenharmony_ci}
16388c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(cpr_debug_info);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic void cpr_debugfs_init(struct cpr_drv *drv)
16418c2ecf20Sopenharmony_ci{
16428c2ecf20Sopenharmony_ci	drv->debugfs = debugfs_create_dir("qcom_cpr", NULL);
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	debugfs_create_file("debug_info", 0444, drv->debugfs,
16458c2ecf20Sopenharmony_ci			    drv, &cpr_debug_info_fops);
16468c2ecf20Sopenharmony_ci}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cistatic int cpr_probe(struct platform_device *pdev)
16498c2ecf20Sopenharmony_ci{
16508c2ecf20Sopenharmony_ci	struct resource *res;
16518c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
16528c2ecf20Sopenharmony_ci	struct cpr_drv *drv;
16538c2ecf20Sopenharmony_ci	int irq, ret;
16548c2ecf20Sopenharmony_ci	const struct cpr_acc_desc *data;
16558c2ecf20Sopenharmony_ci	struct device_node *np;
16568c2ecf20Sopenharmony_ci	u32 cpr_rev = FUSE_REVISION_UNKNOWN;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	data = of_device_get_match_data(dev);
16598c2ecf20Sopenharmony_ci	if (!data || !data->cpr_desc || !data->acc_desc)
16608c2ecf20Sopenharmony_ci		return -EINVAL;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
16638c2ecf20Sopenharmony_ci	if (!drv)
16648c2ecf20Sopenharmony_ci		return -ENOMEM;
16658c2ecf20Sopenharmony_ci	drv->dev = dev;
16668c2ecf20Sopenharmony_ci	drv->desc = data->cpr_desc;
16678c2ecf20Sopenharmony_ci	drv->acc_desc = data->acc_desc;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	drv->fuse_corners = devm_kcalloc(dev, drv->desc->num_fuse_corners,
16708c2ecf20Sopenharmony_ci					 sizeof(*drv->fuse_corners),
16718c2ecf20Sopenharmony_ci					 GFP_KERNEL);
16728c2ecf20Sopenharmony_ci	if (!drv->fuse_corners)
16738c2ecf20Sopenharmony_ci		return -ENOMEM;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->of_node, "acc-syscon", 0);
16768c2ecf20Sopenharmony_ci	if (!np)
16778c2ecf20Sopenharmony_ci		return -ENODEV;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	drv->tcsr = syscon_node_to_regmap(np);
16808c2ecf20Sopenharmony_ci	of_node_put(np);
16818c2ecf20Sopenharmony_ci	if (IS_ERR(drv->tcsr))
16828c2ecf20Sopenharmony_ci		return PTR_ERR(drv->tcsr);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
16858c2ecf20Sopenharmony_ci	drv->base = devm_ioremap_resource(dev, res);
16868c2ecf20Sopenharmony_ci	if (IS_ERR(drv->base))
16878c2ecf20Sopenharmony_ci		return PTR_ERR(drv->base);
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
16908c2ecf20Sopenharmony_ci	if (irq < 0)
16918c2ecf20Sopenharmony_ci		return -EINVAL;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	drv->vdd_apc = devm_regulator_get(dev, "vdd-apc");
16948c2ecf20Sopenharmony_ci	if (IS_ERR(drv->vdd_apc))
16958c2ecf20Sopenharmony_ci		return PTR_ERR(drv->vdd_apc);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	/*
16988c2ecf20Sopenharmony_ci	 * Initialize fuse corners, since it simply depends
16998c2ecf20Sopenharmony_ci	 * on data in efuses.
17008c2ecf20Sopenharmony_ci	 * Everything related to (virtual) corners has to be
17018c2ecf20Sopenharmony_ci	 * initialized after attaching to the power domain,
17028c2ecf20Sopenharmony_ci	 * since it depends on the CPU's OPP table.
17038c2ecf20Sopenharmony_ci	 */
17048c2ecf20Sopenharmony_ci	ret = cpr_read_efuse(dev, "cpr_fuse_revision", &cpr_rev);
17058c2ecf20Sopenharmony_ci	if (ret)
17068c2ecf20Sopenharmony_ci		return ret;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	drv->cpr_fuses = cpr_get_fuses(drv);
17098c2ecf20Sopenharmony_ci	if (IS_ERR(drv->cpr_fuses))
17108c2ecf20Sopenharmony_ci		return PTR_ERR(drv->cpr_fuses);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	ret = cpr_populate_ring_osc_idx(drv);
17138c2ecf20Sopenharmony_ci	if (ret)
17148c2ecf20Sopenharmony_ci		return ret;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	ret = cpr_fuse_corner_init(drv);
17178c2ecf20Sopenharmony_ci	if (ret)
17188c2ecf20Sopenharmony_ci		return ret;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	mutex_init(&drv->lock);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(dev, irq, NULL,
17238c2ecf20Sopenharmony_ci					cpr_irq_handler,
17248c2ecf20Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
17258c2ecf20Sopenharmony_ci					"cpr", drv);
17268c2ecf20Sopenharmony_ci	if (ret)
17278c2ecf20Sopenharmony_ci		return ret;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	drv->pd.name = devm_kstrdup_const(dev, dev->of_node->full_name,
17308c2ecf20Sopenharmony_ci					  GFP_KERNEL);
17318c2ecf20Sopenharmony_ci	if (!drv->pd.name)
17328c2ecf20Sopenharmony_ci		return -EINVAL;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	drv->pd.power_off = cpr_power_off;
17358c2ecf20Sopenharmony_ci	drv->pd.power_on = cpr_power_on;
17368c2ecf20Sopenharmony_ci	drv->pd.set_performance_state = cpr_set_performance_state;
17378c2ecf20Sopenharmony_ci	drv->pd.opp_to_performance_state = cpr_get_performance_state;
17388c2ecf20Sopenharmony_ci	drv->pd.attach_dev = cpr_pd_attach_dev;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	ret = pm_genpd_init(&drv->pd, NULL, true);
17418c2ecf20Sopenharmony_ci	if (ret)
17428c2ecf20Sopenharmony_ci		return ret;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd);
17458c2ecf20Sopenharmony_ci	if (ret)
17468c2ecf20Sopenharmony_ci		goto err_remove_genpd;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, drv);
17498c2ecf20Sopenharmony_ci	cpr_debugfs_init(drv);
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	return 0;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_cierr_remove_genpd:
17548c2ecf20Sopenharmony_ci	pm_genpd_remove(&drv->pd);
17558c2ecf20Sopenharmony_ci	return ret;
17568c2ecf20Sopenharmony_ci}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_cistatic int cpr_remove(struct platform_device *pdev)
17598c2ecf20Sopenharmony_ci{
17608c2ecf20Sopenharmony_ci	struct cpr_drv *drv = platform_get_drvdata(pdev);
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	if (cpr_is_allowed(drv)) {
17638c2ecf20Sopenharmony_ci		cpr_ctl_disable(drv);
17648c2ecf20Sopenharmony_ci		cpr_irq_set(drv, 0);
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	of_genpd_del_provider(pdev->dev.of_node);
17688c2ecf20Sopenharmony_ci	pm_genpd_remove(&drv->pd);
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	debugfs_remove_recursive(drv->debugfs);
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	return 0;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic const struct of_device_id cpr_match_table[] = {
17768c2ecf20Sopenharmony_ci	{ .compatible = "qcom,qcs404-cpr", .data = &qcs404_cpr_acc_desc },
17778c2ecf20Sopenharmony_ci	{ }
17788c2ecf20Sopenharmony_ci};
17798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpr_match_table);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic struct platform_driver cpr_driver = {
17828c2ecf20Sopenharmony_ci	.probe		= cpr_probe,
17838c2ecf20Sopenharmony_ci	.remove		= cpr_remove,
17848c2ecf20Sopenharmony_ci	.driver		= {
17858c2ecf20Sopenharmony_ci		.name	= "qcom-cpr",
17868c2ecf20Sopenharmony_ci		.of_match_table = cpr_match_table,
17878c2ecf20Sopenharmony_ci	},
17888c2ecf20Sopenharmony_ci};
17898c2ecf20Sopenharmony_cimodule_platform_driver(cpr_driver);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Core Power Reduction (CPR) driver");
17928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1793