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, "_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