soc/qualcomm/common: Add RPMh driver support
Add RPMh driver support, introducing the core driver that provides an interface to the RPMh protocol for managing ARC/VRM/BCM type resource requests. This includes basic TCS (Trigger Command Sets) handling and helper functions for sending RPMh requests. RPMh (Resource Power Manager – hardware) is a protocol that enables processors (e.g., APSS, LPASS) to send power-related commands to the RPMh hardware block. Dynamic management of power and clocks for shared resources is handled either directly by hardware or by RPM. Key features include: - Core infrastructure for submitting TCS (Trigger Command Sets) commands to the RPMh. - Regulator driver using RPMh for LDOs and SMPS control. - BCM (Bus Clock Manager) voting for clock resources. Test=Create an image.serial.bin and ensure it boots on X1P42100. Change-Id: I1f85459c68d0256e15765b0716856dc928080df9 Signed-off-by: Swathi Tamilselvan <tswathi@qualcomm.corp-partner.google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/90466 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Kapil Porwal <kapilporwal@google.com>
This commit is contained in:
parent
999dd8905a
commit
dc162f84be
11 changed files with 2078 additions and 0 deletions
25
src/soc/qualcomm/common/include/soc/rpmh.h
Normal file
25
src/soc/qualcomm/common/include/soc/rpmh.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_RPMH_H__
|
||||
#define __SOC_QUALCOMM_RPMH_H__
|
||||
|
||||
#include <device/device.h>
|
||||
#include <soc/tcs.h>
|
||||
|
||||
int rpmh_write(enum rpmh_state state, const struct tcs_cmd *cmd, u32 n);
|
||||
|
||||
int rpmh_write_async(enum rpmh_state state, const struct tcs_cmd *cmd, u32 n);
|
||||
|
||||
int rpmh_write_batch(enum rpmh_state state, const struct tcs_cmd *cmd, u32 *n);
|
||||
|
||||
void rpmh_invalidate(void);
|
||||
|
||||
int rpmh_write_sleep_and_wake(void);
|
||||
|
||||
int rpmh_mode_solver_set(bool enable);
|
||||
|
||||
int rpmh_init_fast_path(struct tcs_cmd *cmd, int n);
|
||||
|
||||
int rpmh_update_fast_path(struct tcs_cmd *cmd, int n, u32 update_mask);
|
||||
|
||||
#endif /* __SOC_QUALCOMM_RPMH_H__ */
|
||||
17
src/soc/qualcomm/common/include/soc/rpmh_bcm.h
Normal file
17
src/soc/qualcomm/common/include/soc/rpmh_bcm.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_RPMH_BCM_H__
|
||||
#define __SOC_QUALCOMM_RPMH_BCM_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/**
|
||||
* rpmh_bcm_vote() - Send BCM (Bus Clock Manager) vote to RPMh
|
||||
* @resource_name: BCM resource name
|
||||
* @vote_value: Vote value to send
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure
|
||||
*/
|
||||
int rpmh_bcm_vote(const char *resource_name, u32 vote_value);
|
||||
|
||||
#endif /* __SOC_QUALCOMM_RPMH_BCM_H__ */
|
||||
220
src/soc/qualcomm/common/include/soc/rpmh_internal.h
Normal file
220
src/soc/qualcomm/common/include/soc/rpmh_internal.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_RPMH_INTERNAL_H__
|
||||
#define __SOC_QUALCOMM_RPMH_INTERNAL_H__
|
||||
|
||||
#include <types.h>
|
||||
#include <soc/tcs.h>
|
||||
|
||||
#define MAX_NAME_LENGTH 20
|
||||
|
||||
#define USECS_1S 1000000
|
||||
#define USECS_100MS 100000
|
||||
#define USECS_10MS 10000
|
||||
#define USECS_100US 100
|
||||
#define USECS_5US 5
|
||||
#define LOOP_DELAY_US 1
|
||||
#define LOOP_DELAY_10US 10
|
||||
|
||||
/* DRV Channel IDs */
|
||||
enum {
|
||||
CH0,
|
||||
CH1,
|
||||
MAX_CHANNEL,
|
||||
};
|
||||
|
||||
#define TCS_TYPE_NR 5
|
||||
#define MAX_CMDS_PER_TCS 16
|
||||
#define MAX_TCS_PER_TYPE 3
|
||||
#define MAX_TCS_NR (MAX_TCS_PER_TYPE * TCS_TYPE_NR)
|
||||
#define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE)
|
||||
|
||||
#define SOLVER_PRESENT 1
|
||||
#define HW_CHANNEL_PRESENT 2
|
||||
|
||||
#define CMD_DB_MAX_RESOURCES 250
|
||||
|
||||
/* TCS types */
|
||||
enum {
|
||||
ACTIVE_TCS,
|
||||
SLEEP_TCS,
|
||||
WAKE_TCS,
|
||||
CONTROL_TCS,
|
||||
FAST_PATH_TCS,
|
||||
};
|
||||
|
||||
/* RSC register offsets */
|
||||
enum {
|
||||
RSC_DRV_TCS_OFFSET,
|
||||
RSC_DRV_CMD_OFFSET,
|
||||
DRV_SOLVER_CONFIG,
|
||||
DRV_PRNT_CHLD_CONFIG,
|
||||
RSC_DRV_IRQ_ENABLE,
|
||||
RSC_DRV_IRQ_STATUS,
|
||||
RSC_DRV_IRQ_CLEAR,
|
||||
RSC_DRV_CMD_WAIT_FOR_CMPL,
|
||||
RSC_DRV_CONTROL,
|
||||
RSC_DRV_STATUS,
|
||||
RSC_DRV_CMD_ENABLE,
|
||||
RSC_DRV_CMD_MSGID,
|
||||
RSC_DRV_CMD_ADDR,
|
||||
RSC_DRV_CMD_DATA,
|
||||
RSC_DRV_CMD_STATUS,
|
||||
RSC_DRV_CMD_RESP_DATA,
|
||||
RSC_DRV_CHN_TCS_TRIGGER,
|
||||
RSC_DRV_CHN_TCS_COMPLETE,
|
||||
RSC_DRV_CHN_SEQ_BUSY,
|
||||
RSC_DRV_CHN_SEQ_PC,
|
||||
RSC_DRV_CHN_UPDATE,
|
||||
RSC_DRV_CHN_BUSY,
|
||||
RSC_DRV_CHN_EN,
|
||||
};
|
||||
|
||||
/* DRV TCS Configuration Information Register */
|
||||
#define DRV_NUM_TCS_MASK 0x3F
|
||||
#define DRV_NUM_TCS_SHIFT 6
|
||||
#define DRV_NCPT_MASK 0x1F
|
||||
#define DRV_NCPT_SHIFT 27
|
||||
|
||||
struct rsc_drv;
|
||||
|
||||
/**
|
||||
* struct cache_req: the request object for caching
|
||||
*
|
||||
* @addr: the address of the resource
|
||||
* @sleep_val: the sleep vote
|
||||
* @wake_val: the wake vote
|
||||
*/
|
||||
struct cache_req {
|
||||
u32 addr;
|
||||
u32 sleep_val;
|
||||
u32 wake_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tcs_group: group of Trigger Command Sets (TCS) to send state requests
|
||||
* to the controller
|
||||
*
|
||||
* @drv: The controller.
|
||||
* @type: Type of the TCS in this group - active, sleep, wake.
|
||||
* @mask: Mask of the TCSes relative to all the TCSes in the RSC.
|
||||
* @offset: Start of the TCS group relative to the TCSes in the RSC.
|
||||
* @num_tcs: Number of TCSes in this type.
|
||||
* @ncpt: Number of commands in each TCS.
|
||||
* @req: Requests that are sent from the TCS.
|
||||
* @slots: Bitmap indicating which slots are occupied.
|
||||
*/
|
||||
struct tcs_group {
|
||||
struct rsc_drv *drv;
|
||||
int type;
|
||||
u32 mask;
|
||||
u32 offset;
|
||||
int num_tcs;
|
||||
int ncpt;
|
||||
const struct tcs_request *req[MAX_TCS_PER_TYPE];
|
||||
u32 slots[MAX_TCS_SLOTS / 32];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmh_request: the message to be sent to rpmh-rsc
|
||||
*
|
||||
* @msg: the request
|
||||
* @cmd: the payload that will be part of the @msg
|
||||
*/
|
||||
struct rpmh_request {
|
||||
struct tcs_request msg;
|
||||
struct tcs_cmd cmd[MAX_RPMH_PAYLOAD];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmh_ctrlr: our representation of the controller
|
||||
*
|
||||
* @dirty: was the cache updated since flush
|
||||
* @in_solver_mode: Controller is busy in solver mode
|
||||
* @flags: Controller specific flags
|
||||
* @batch_cache: Cache sleep and wake requests sent as batch
|
||||
* @non_batch_cache_idx: Index for non-batch cache
|
||||
* @non_batch_cache: Cache for non-batch requests
|
||||
*/
|
||||
struct rpmh_ctrlr {
|
||||
bool dirty;
|
||||
bool in_solver_mode;
|
||||
u32 flags;
|
||||
struct rpmh_request batch_cache[RPMH_ACTIVE_ONLY_STATE];
|
||||
u32 non_batch_cache_idx;
|
||||
struct cache_req *non_batch_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drv_channel: our representation of the drv channels
|
||||
*
|
||||
* @tcs: TCS groups
|
||||
* @drv: DRV containing the channel
|
||||
* @initialized: Whether channel is initialized
|
||||
*/
|
||||
struct drv_channel {
|
||||
struct tcs_group tcs[TCS_TYPE_NR];
|
||||
struct rsc_drv *drv;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rsc_drv: the Direct Resource Voter (DRV) of the
|
||||
* Resource State Coordinator controller (RSC)
|
||||
*
|
||||
* @name: Controller identifier
|
||||
* @base: Start address of the DRV registers in this controller
|
||||
* @tcs_base: Start address of the TCS registers in this controller
|
||||
* @tcs_distance: Distance between two TCSes
|
||||
* @id: Instance id in the controller (Direct Resource Voter)
|
||||
* @num_tcs: Number of TCSes in this DRV
|
||||
* @num_channels: Number of channels in this DRV
|
||||
* @in_solver_mode: Controller is busy in solver mode
|
||||
* @initialized: Whether DRV is initialized
|
||||
* @tcs: TCS groups
|
||||
* @ch: DRV channels
|
||||
* @tcs_in_use: S/W state of the TCS bitmap
|
||||
* @client: Handle to the DRV's client
|
||||
* @dev: RSC device
|
||||
* @regs: Register offsets for RSC controller
|
||||
*/
|
||||
struct rsc_drv {
|
||||
char name[MAX_NAME_LENGTH];
|
||||
void *base;
|
||||
void *tcs_base;
|
||||
u32 tcs_distance;
|
||||
unsigned int id;
|
||||
int num_tcs;
|
||||
int num_channels;
|
||||
bool in_solver_mode;
|
||||
bool initialized;
|
||||
struct tcs_group tcs[TCS_TYPE_NR];
|
||||
struct drv_channel ch[MAX_CHANNEL];
|
||||
u32 tcs_in_use[(MAX_TCS_NR + 31) / 32];
|
||||
struct rpmh_ctrlr client;
|
||||
struct device *dev;
|
||||
u32 *regs;
|
||||
};
|
||||
|
||||
extern bool rpmh_standalone;
|
||||
|
||||
int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg, int ch);
|
||||
int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv,
|
||||
const struct tcs_request *msg,
|
||||
int ch);
|
||||
void rpmh_rsc_invalidate(struct rsc_drv *drv, int ch);
|
||||
int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable);
|
||||
int rpmh_rsc_get_channel(struct rsc_drv *drv);
|
||||
int rpmh_rsc_switch_channel(struct rsc_drv *drv, int ch);
|
||||
int rpmh_rsc_drv_enable(struct rsc_drv *drv, bool enable);
|
||||
|
||||
int rpmh_flush(struct rpmh_ctrlr *ctrlr, int ch);
|
||||
|
||||
int rpmh_rsc_init_fast_path(struct rsc_drv *drv, const struct tcs_request *msg, int ch);
|
||||
int rpmh_rsc_update_fast_path(struct rsc_drv *drv,
|
||||
const struct tcs_request *msg,
|
||||
u32 update_mask, int ch);
|
||||
|
||||
void rpmh_set_rsc_drv(struct rsc_drv *drv);
|
||||
|
||||
#endif /* __SOC_QUALCOMM_RPMH_INTERNAL_H__ */
|
||||
220
src/soc/qualcomm/common/include/soc/rpmh_regulator.h
Normal file
220
src/soc/qualcomm/common/include/soc/rpmh_regulator.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_RPMH_REGULATOR_H__
|
||||
#define __SOC_QUALCOMM_RPMH_REGULATOR_H__
|
||||
|
||||
#include <types.h>
|
||||
#include <device/device.h>
|
||||
|
||||
#define RPMH_ARC_LEVEL_SIZE 2
|
||||
#define RPMH_ARC_MAX_LEVELS 32
|
||||
#define RPMH_REGULATOR_LEVEL_OFF 0
|
||||
|
||||
/* Min and max limits of VRM resource request parameters */
|
||||
#define RPMH_VRM_MIN_UV 0
|
||||
#define RPMH_VRM_MAX_UV 8191000
|
||||
#define RPMH_VRM_STEP_UV 1000
|
||||
#define RPMH_VRM_HEADROOM_MIN_UV 0
|
||||
#define RPMH_VRM_HEADROOM_MAX_UV 511000
|
||||
|
||||
#define RPMH_VRM_MODE_MIN 0
|
||||
#define RPMH_VRM_MODE_MAX 7
|
||||
|
||||
/* XOB and PBS voting registers are found in the VRM hardware module */
|
||||
#define CMD_DB_HW_XOB CMD_DB_HW_VRM
|
||||
#define CMD_DB_HW_PBS CMD_DB_HW_VRM
|
||||
|
||||
/* Voltage unknown placeholder */
|
||||
#define VOLTAGE_UNKNOWN 1
|
||||
|
||||
/**
|
||||
* enum rpmh_regulator_type - supported RPMh accelerator types
|
||||
* @RPMH_REGULATOR_TYPE_VRM: RPMh VRM accelerator which supports voting on
|
||||
* enable, voltage, mode, and headroom voltage of
|
||||
* LDO, SMPS, VS, and BOB type PMIC regulators.
|
||||
* @RPMH_REGULATOR_TYPE_ARC: RPMh ARC accelerator which supports voting on
|
||||
* the CPR managed voltage level of LDO and SMPS
|
||||
* type PMIC regulators.
|
||||
* @RPMH_REGULATOR_TYPE_XOB: RPMh XOB accelerator which supports voting on
|
||||
* the enable state of PMIC regulators.
|
||||
* @RPMH_REGULATOR_TYPE_PBS: RPMh PBS accelerator which supports voting on
|
||||
* the enable state of PBS resources.
|
||||
* @RPMH_REGULATOR_TYPE_BCM: RPMh BCM (Bus Clock Manager) accelerator which
|
||||
* supports voting on bandwidth and clock frequency
|
||||
* requirements for interconnect and bus resources.
|
||||
*/
|
||||
enum rpmh_regulator_type {
|
||||
RPMH_REGULATOR_TYPE_VRM,
|
||||
RPMH_REGULATOR_TYPE_ARC,
|
||||
RPMH_REGULATOR_TYPE_XOB,
|
||||
RPMH_REGULATOR_TYPE_PBS,
|
||||
RPMH_REGULATOR_TYPE_BCM,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum rpmh_regulator_reg_index - RPMh accelerator register indices
|
||||
* @RPMH_REGULATOR_REG_VRM_VOLTAGE: VRM voltage voting register index
|
||||
* @RPMH_REGULATOR_REG_ARC_LEVEL: ARC voltage level voting register index
|
||||
* @RPMH_REGULATOR_REG_PBS_ENABLE: PBS enable voting register index
|
||||
* @RPMH_REGULATOR_REG_VRM_ENABLE: VRM enable voltage voting register index
|
||||
* @RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE: Place-holder for enable aggregation
|
||||
* @RPMH_REGULATOR_REG_XOB_ENABLE: XOB enable voting register index
|
||||
* @RPMH_REGULATOR_REG_ENABLE: Common enable index
|
||||
* @RPMH_REGULATOR_REG_VRM_MODE: VRM regulator mode voting register index
|
||||
* @RPMH_REGULATOR_REG_VRM_HEADROOM: VRM headroom voltage voting register index
|
||||
* @RPMH_REGULATOR_REG_MAX: Maximum number of registers
|
||||
*/
|
||||
enum rpmh_regulator_reg_index {
|
||||
RPMH_REGULATOR_REG_VRM_VOLTAGE = 0,
|
||||
RPMH_REGULATOR_REG_ARC_LEVEL = 0,
|
||||
RPMH_REGULATOR_REG_PBS_ENABLE = 0,
|
||||
RPMH_REGULATOR_REG_VRM_ENABLE = 1,
|
||||
RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE = 1,
|
||||
RPMH_REGULATOR_REG_XOB_ENABLE = 1,
|
||||
RPMH_REGULATOR_REG_ENABLE = 1,
|
||||
RPMH_REGULATOR_REG_VRM_MODE = 2,
|
||||
RPMH_REGULATOR_REG_VRM_HEADROOM = 3,
|
||||
RPMH_REGULATOR_REG_MAX = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmh_regulator_request - internal request structure
|
||||
*/
|
||||
struct rpmh_regulator_request {
|
||||
u32 reg[RPMH_REGULATOR_REG_MAX];
|
||||
u32 valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmh_vreg - individual rpmh regulator data structure
|
||||
* @resource_name: Name of rpmh regulator resource
|
||||
* @addr: Base address of the regulator resource within an RPMh accelerator
|
||||
* @type: RPMh accelerator type for this regulator resource
|
||||
* @level: Mapping from ARC resource specific voltage levels to consumer levels
|
||||
* @level_count: The number of valid entries in the level array
|
||||
* @req_active: Active set RPMh accelerator register request
|
||||
* @req_sleep: Sleep set RPMh accelerator register request
|
||||
*/
|
||||
|
||||
struct rpmh_vreg {
|
||||
const char *resource_name;
|
||||
u32 addr;
|
||||
enum rpmh_regulator_type type;
|
||||
u32 level[RPMH_ARC_MAX_LEVELS];
|
||||
int level_count;
|
||||
struct rpmh_regulator_request req_active;
|
||||
struct rpmh_regulator_request req_sleep;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* rpmh_regulator_init() - initialize an rpmh regulator
|
||||
* @vreg: Pointer to the rpmh regulator structure to initialize
|
||||
* @dev: Device pointer for RPMh communication
|
||||
* @resource_name: Name of the RPMh resource (e.g., "ldoa1")
|
||||
* @type: Type of regulator (VRM, ARC, XOB, PBS)
|
||||
*
|
||||
* This function initializes an RPMh regulator by looking up its address
|
||||
* in the command DB and loading any necessary configuration data.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_init(struct rpmh_vreg *vreg, const char *resource_name,
|
||||
enum rpmh_regulator_type type);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_vrm_set_voltage() - set the voltage of a VRM regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @min_uv: Minimum voltage in microvolts
|
||||
* @max_uv: Maximum voltage in microvolts
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* This function sets the voltage for a VRM type regulator. The actual
|
||||
* voltage set will be the minimum value that satisfies min_uv.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_vrm_set_voltage(struct rpmh_vreg *vreg, int min_uv, int max_uv,
|
||||
bool set_active, bool set_sleep);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_vrm_set_mode() - set the mode of a VRM regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @mode: PMIC mode value (use RPMH_REGULATOR_MODE_* defines)
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* This function sets the operating mode for a VRM type regulator.
|
||||
* The mode value should be one of the RPMH_REGULATOR_MODE_PMIC*_* defines
|
||||
* appropriate for the regulator type.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_vrm_set_mode(struct rpmh_vreg *vreg, u32 mode,
|
||||
bool set_active, bool set_sleep);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_enable() - enable a regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* This function enables the regulator. Works for VRM, ARC, XOB, and PBS types.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_enable(struct rpmh_vreg *vreg, bool set_active, bool set_sleep);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_disable() - disable a regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* This function disables the regulator. Works for VRM, ARC, XOB, and PBS types.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_disable(struct rpmh_vreg *vreg, bool set_active, bool set_sleep);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_set_level() - set the voltage level of an ARC regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @level: ARC voltage level (0 to level_count-1)
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* This function sets the voltage level for an ARC type regulator.
|
||||
* The level is an index into the voltage level table loaded from command DB.
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_arc_set_level(struct rpmh_vreg *vreg, u32 level,
|
||||
bool set_active, bool set_sleep);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_get_level() - get the current voltage level of an ARC regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
*
|
||||
* This function returns the cached ARC voltage level that was last set.
|
||||
* Note: This returns the requested level, not necessarily the actual hardware level,
|
||||
* as there's no way to read back from RPMh hardware.
|
||||
*
|
||||
* Return: ARC voltage level (0 to level_count-1) on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_arc_get_level(struct rpmh_vreg *vreg);
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_get_voltage_level() - get the voltage level value for an ARC level
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @level: ARC level index
|
||||
*
|
||||
* This function returns the actual voltage level value (vlvl) that corresponds
|
||||
* to the given ARC hardware level (hlvl).
|
||||
*
|
||||
* Return: voltage level value on success, 0 on failure
|
||||
*/
|
||||
u32 rpmh_regulator_arc_get_voltage_level(struct rpmh_vreg *vreg, u32 level);
|
||||
|
||||
#endif /* __SOC_QUALCOMM_RPMH_REGULATOR_H__ */
|
||||
87
src/soc/qualcomm/common/include/soc/rpmh_rsc.h
Normal file
87
src/soc/qualcomm/common/include/soc/rpmh_rsc.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_RPMH_RSC_H__
|
||||
#define __SOC_QUALCOMM_RPMH_RSC_H__
|
||||
|
||||
#include <types.h>
|
||||
#include <soc/rpmh_internal.h>
|
||||
|
||||
u32 rpmh_rsc_reg_offset_ver_2_7[] = {
|
||||
[RSC_DRV_TCS_OFFSET] = 672,
|
||||
[RSC_DRV_CMD_OFFSET] = 20,
|
||||
[DRV_SOLVER_CONFIG] = 0x04,
|
||||
[DRV_PRNT_CHLD_CONFIG] = 0x0C,
|
||||
[RSC_DRV_IRQ_ENABLE] = 0x00,
|
||||
[RSC_DRV_IRQ_STATUS] = 0x04,
|
||||
[RSC_DRV_IRQ_CLEAR] = 0x08,
|
||||
[RSC_DRV_CMD_WAIT_FOR_CMPL] = 0x10,
|
||||
[RSC_DRV_CONTROL] = 0x14,
|
||||
[RSC_DRV_STATUS] = 0x18,
|
||||
[RSC_DRV_CMD_ENABLE] = 0x1C,
|
||||
[RSC_DRV_CMD_MSGID] = 0x30,
|
||||
[RSC_DRV_CMD_ADDR] = 0x34,
|
||||
[RSC_DRV_CMD_DATA] = 0x38,
|
||||
[RSC_DRV_CMD_STATUS] = 0x3C,
|
||||
[RSC_DRV_CMD_RESP_DATA] = 0x40,
|
||||
[RSC_DRV_CHN_SEQ_BUSY] = 0x0,
|
||||
[RSC_DRV_CHN_SEQ_PC] = 0x0,
|
||||
[RSC_DRV_CHN_TCS_TRIGGER] = 0x0,
|
||||
[RSC_DRV_CHN_TCS_COMPLETE] = 0x0,
|
||||
[RSC_DRV_CHN_UPDATE] = 0x0,
|
||||
[RSC_DRV_CHN_BUSY] = 0x0,
|
||||
[RSC_DRV_CHN_EN] = 0x0,
|
||||
};
|
||||
|
||||
u32 rpmh_rsc_reg_offset_ver_3_0[] = {
|
||||
[RSC_DRV_TCS_OFFSET] = 672,
|
||||
[RSC_DRV_CMD_OFFSET] = 24,
|
||||
[DRV_SOLVER_CONFIG] = 0x04,
|
||||
[DRV_PRNT_CHLD_CONFIG] = 0x0C,
|
||||
[RSC_DRV_IRQ_ENABLE] = 0x00,
|
||||
[RSC_DRV_IRQ_STATUS] = 0x04,
|
||||
[RSC_DRV_IRQ_CLEAR] = 0x08,
|
||||
[RSC_DRV_CMD_WAIT_FOR_CMPL] = 0x20,
|
||||
[RSC_DRV_CONTROL] = 0x24,
|
||||
[RSC_DRV_STATUS] = 0x28,
|
||||
[RSC_DRV_CMD_ENABLE] = 0x2C,
|
||||
[RSC_DRV_CMD_MSGID] = 0x34,
|
||||
[RSC_DRV_CMD_ADDR] = 0x38,
|
||||
[RSC_DRV_CMD_DATA] = 0x3C,
|
||||
[RSC_DRV_CMD_STATUS] = 0x40,
|
||||
[RSC_DRV_CMD_RESP_DATA] = 0x44,
|
||||
[RSC_DRV_CHN_SEQ_BUSY] = 0x464,
|
||||
[RSC_DRV_CHN_SEQ_PC] = 0x468,
|
||||
[RSC_DRV_CHN_TCS_TRIGGER] = 0x490,
|
||||
[RSC_DRV_CHN_TCS_COMPLETE] = 0x494,
|
||||
[RSC_DRV_CHN_UPDATE] = 0x498,
|
||||
[RSC_DRV_CHN_BUSY] = 0x49C,
|
||||
[RSC_DRV_CHN_EN] = 0x4A0,
|
||||
};
|
||||
|
||||
u32 rpmh_rsc_reg_offset_ver_3_0_hw_channel[] = {
|
||||
[RSC_DRV_TCS_OFFSET] = 336,
|
||||
[RSC_DRV_CMD_OFFSET] = 24,
|
||||
[DRV_SOLVER_CONFIG] = 0x04,
|
||||
[DRV_PRNT_CHLD_CONFIG] = 0x0C,
|
||||
[RSC_DRV_IRQ_ENABLE] = 0x00,
|
||||
[RSC_DRV_IRQ_STATUS] = 0x04,
|
||||
[RSC_DRV_IRQ_CLEAR] = 0x08,
|
||||
[RSC_DRV_CMD_WAIT_FOR_CMPL] = 0x20,
|
||||
[RSC_DRV_CONTROL] = 0x24,
|
||||
[RSC_DRV_STATUS] = 0x28,
|
||||
[RSC_DRV_CMD_ENABLE] = 0x2C,
|
||||
[RSC_DRV_CMD_MSGID] = 0x34,
|
||||
[RSC_DRV_CMD_ADDR] = 0x38,
|
||||
[RSC_DRV_CMD_DATA] = 0x3C,
|
||||
[RSC_DRV_CMD_STATUS] = 0x40,
|
||||
[RSC_DRV_CMD_RESP_DATA] = 0x44,
|
||||
[RSC_DRV_CHN_SEQ_BUSY] = 0x464,
|
||||
[RSC_DRV_CHN_SEQ_PC] = 0x468,
|
||||
[RSC_DRV_CHN_TCS_TRIGGER] = 0x490,
|
||||
[RSC_DRV_CHN_TCS_COMPLETE] = 0x494,
|
||||
[RSC_DRV_CHN_UPDATE] = 0x498,
|
||||
[RSC_DRV_CHN_BUSY] = 0x49C,
|
||||
[RSC_DRV_CHN_EN] = 0x4A0,
|
||||
};
|
||||
|
||||
#endif /* __SOC_QUALCOMM_RPMH_RSC_H__ */
|
||||
72
src/soc/qualcomm/common/include/soc/tcs.h
Normal file
72
src/soc/qualcomm/common/include/soc/tcs.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_QUALCOMM_TCS_H__
|
||||
#define __SOC_QUALCOMM_TCS_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#define MAX_RPMH_PAYLOAD 16
|
||||
|
||||
/**
|
||||
* rpmh_state: state for the request
|
||||
*
|
||||
* RPMH_SLEEP_STATE: State of the resource when the processor subsystem
|
||||
* is powered down. There is no client using the
|
||||
* resource actively.
|
||||
* RPMH_WAKE_ONLY_STATE: Resume resource state to the value previously
|
||||
* requested before the processor was powered down.
|
||||
* RPMH_ACTIVE_ONLY_STATE: Active or AMC mode requests. Resource state
|
||||
* is aggregated immediately.
|
||||
*/
|
||||
enum rpmh_state {
|
||||
RPMH_SLEEP_STATE,
|
||||
RPMH_WAKE_ONLY_STATE,
|
||||
RPMH_ACTIVE_ONLY_STATE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tcs_cmd: an individual request to RPMH.
|
||||
*
|
||||
* @addr: the address of the resource slv_id:18:16 | offset:0:15
|
||||
* @data: the resource state request
|
||||
* @wait: ensure that this command is complete before returning.
|
||||
*/
|
||||
struct tcs_cmd {
|
||||
u32 addr;
|
||||
u32 data;
|
||||
u32 wait;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tcs_request: A set of tcs_cmds sent together in a TCS
|
||||
*
|
||||
* @state: state for the request.
|
||||
* @wait_for_compl: wait until we get a response from the h/w accelerator
|
||||
* @num_cmds: the number of @cmds in this request
|
||||
* @cmds: an array of tcs_cmds
|
||||
*/
|
||||
struct tcs_request {
|
||||
enum rpmh_state state;
|
||||
u32 wait_for_compl;
|
||||
u32 num_cmds;
|
||||
struct tcs_cmd *cmds;
|
||||
};
|
||||
|
||||
#define BCM_TCS_CMD_COMMIT_SHFT 30
|
||||
#define BCM_TCS_CMD_COMMIT_MASK 0x40000000
|
||||
#define BCM_TCS_CMD_VALID_SHFT 29
|
||||
#define BCM_TCS_CMD_VALID_MASK 0x20000000
|
||||
#define BCM_TCS_CMD_VOTE_X_SHFT 14
|
||||
#define BCM_TCS_CMD_VOTE_MASK 0x3fff
|
||||
#define BCM_TCS_CMD_VOTE_Y_SHFT 0
|
||||
#define BCM_TCS_CMD_VOTE_Y_MASK 0xfffc000
|
||||
|
||||
/* Construct a Bus Clock Manager (BCM) specific TCS command */
|
||||
#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \
|
||||
cpu_to_le32( \
|
||||
(((uint32_t)(commit) & 0x1) << BCM_TCS_CMD_COMMIT_SHFT) | \
|
||||
(((uint32_t)(valid) & 0x1) << BCM_TCS_CMD_VALID_SHFT) | \
|
||||
(((uint32_t)(vote_x) & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) | \
|
||||
(((uint32_t)(vote_y) & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT) \
|
||||
)
|
||||
#endif /* __SOC_QUALCOMM_TCS_H__ */
|
||||
408
src/soc/qualcomm/common/rpmh.c
Normal file
408
src/soc/qualcomm/common/rpmh.c
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <delay.h>
|
||||
#include <device/device.h>
|
||||
#include <string.h>
|
||||
#include <soc/rpmh.h>
|
||||
#include <soc/cmd_db.h>
|
||||
#include <soc/rpmh_internal.h>
|
||||
|
||||
#define RPMH_TIMEOUT_MS 10000
|
||||
|
||||
#define DEFINE_RPMH_MSG_ONSTACK(s, name) \
|
||||
struct rpmh_request name = { \
|
||||
.msg = { \
|
||||
.state = s, \
|
||||
.cmds = name.cmd, \
|
||||
.num_cmds = 0, \
|
||||
.wait_for_compl = 1, \
|
||||
}, \
|
||||
.cmd = { { 0 } }, \
|
||||
}
|
||||
|
||||
#define ctrlr_to_drv(ctrlr) \
|
||||
((struct rsc_drv *)((char *)(ctrlr) - offsetof(struct rsc_drv, client)))
|
||||
|
||||
bool rpmh_standalone;
|
||||
|
||||
static struct rsc_drv *g_rsc_drv = NULL;
|
||||
|
||||
static struct rpmh_ctrlr *get_rpmh_ctrlr(void)
|
||||
{
|
||||
return g_rsc_drv ? &g_rsc_drv->client : NULL;
|
||||
}
|
||||
|
||||
void rpmh_set_rsc_drv(struct rsc_drv *drv)
|
||||
{
|
||||
g_rsc_drv = drv;
|
||||
}
|
||||
|
||||
static int check_ctrlr_state(struct rpmh_ctrlr *ctrlr, enum rpmh_state state)
|
||||
{
|
||||
if (state != RPMH_ACTIVE_ONLY_STATE)
|
||||
return 0;
|
||||
|
||||
if (!(ctrlr->flags & SOLVER_PRESENT))
|
||||
return 0;
|
||||
|
||||
if (ctrlr->in_solver_mode)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cache_req *get_non_batch_cache_req(struct rpmh_ctrlr *ctrlr, u32 addr)
|
||||
{
|
||||
struct cache_req *req = ctrlr->non_batch_cache;
|
||||
unsigned int i;
|
||||
|
||||
if (ctrlr->non_batch_cache_idx >= CMD_DB_MAX_RESOURCES)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ctrlr->non_batch_cache_idx; i++) {
|
||||
req = &ctrlr->non_batch_cache[i];
|
||||
if (req->addr == addr)
|
||||
return req;
|
||||
}
|
||||
|
||||
req = &ctrlr->non_batch_cache[ctrlr->non_batch_cache_idx];
|
||||
req->sleep_val = req->wake_val = UINT_MAX;
|
||||
ctrlr->non_batch_cache_idx++;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr,
|
||||
enum rpmh_state state,
|
||||
struct tcs_cmd *cmd)
|
||||
{
|
||||
struct cache_req *req;
|
||||
u32 old_sleep_val, old_wake_val;
|
||||
|
||||
req = get_non_batch_cache_req(ctrlr, cmd->addr);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->addr = cmd->addr;
|
||||
old_sleep_val = req->sleep_val;
|
||||
old_wake_val = req->wake_val;
|
||||
|
||||
switch (state) {
|
||||
case RPMH_ACTIVE_ONLY_STATE:
|
||||
case RPMH_WAKE_ONLY_STATE:
|
||||
req->wake_val = cmd->data;
|
||||
break;
|
||||
case RPMH_SLEEP_STATE:
|
||||
req->sleep_val = cmd->data;
|
||||
break;
|
||||
}
|
||||
|
||||
ctrlr->dirty |= (req->sleep_val != old_sleep_val ||
|
||||
req->wake_val != old_wake_val) &&
|
||||
req->sleep_val != UINT_MAX &&
|
||||
req->wake_val != UINT_MAX;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static int __rpmh_write(enum rpmh_state state, struct rpmh_request *rpm_msg)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ret = -1;
|
||||
struct cache_req *req;
|
||||
int i, ch;
|
||||
|
||||
if (rpmh_standalone)
|
||||
return 0;
|
||||
|
||||
if (!ctrlr)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < rpm_msg->msg.num_cmds; i++) {
|
||||
req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]);
|
||||
if (!req)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state == RPMH_ACTIVE_ONLY_STATE) {
|
||||
ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr));
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg, ch);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
|
||||
const struct tcs_cmd *cmd, u32 num_cmds)
|
||||
{
|
||||
if (!cmd || !num_cmds || num_cmds > MAX_RPMH_PAYLOAD)
|
||||
return -1;
|
||||
|
||||
memcpy(req->cmd, cmd, num_cmds * sizeof(*cmd));
|
||||
|
||||
req->msg.state = state;
|
||||
req->msg.cmds = req->cmd;
|
||||
req->msg.num_cmds = num_cmds;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmh_write_async(enum rpmh_state state, const struct tcs_cmd *cmd, u32 n)
|
||||
{
|
||||
DEFINE_RPMH_MSG_ONSTACK(state, rpm_msg);
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ret;
|
||||
|
||||
if (rpmh_standalone)
|
||||
return 0;
|
||||
|
||||
if (!ctrlr)
|
||||
return -1;
|
||||
|
||||
rpm_msg.msg.wait_for_compl = 0;
|
||||
ret = check_ctrlr_state(ctrlr, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return __rpmh_write(state, &rpm_msg);
|
||||
}
|
||||
|
||||
int rpmh_write(enum rpmh_state state, const struct tcs_cmd *cmd, u32 n)
|
||||
{
|
||||
DEFINE_RPMH_MSG_ONSTACK(state, rpm_msg);
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ret;
|
||||
|
||||
if (rpmh_standalone)
|
||||
return 0;
|
||||
|
||||
if (!ctrlr)
|
||||
return -1;
|
||||
|
||||
ret = check_ctrlr_state(ctrlr, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __rpmh_write(state, &rpm_msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flush_batch(struct rpmh_ctrlr *ctrlr, int ch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Send Sleep/Wake requests to the controller */
|
||||
ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr),
|
||||
&ctrlr->batch_cache[RPMH_SLEEP_STATE].msg, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr),
|
||||
&ctrlr->batch_cache[RPMH_WAKE_ONLY_STATE].msg, ch);
|
||||
}
|
||||
|
||||
int rpmh_write_batch(enum rpmh_state state, const struct tcs_cmd *cmd, u32 *n)
|
||||
{
|
||||
DEFINE_RPMH_MSG_ONSTACK(state, rpm_msg);
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ret, ch;
|
||||
|
||||
if (rpmh_standalone)
|
||||
return 0;
|
||||
|
||||
if (!ctrlr)
|
||||
return -1;
|
||||
|
||||
ret = check_ctrlr_state(ctrlr, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (state == RPMH_ACTIVE_ONLY_STATE) {
|
||||
ret = __fill_rpmh_msg(&rpm_msg, state, cmd, *n);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
memset(&ctrlr->batch_cache[state], 0, sizeof(struct rpmh_request));
|
||||
ret = __fill_rpmh_msg(&ctrlr->batch_cache[state], state, cmd, *n);
|
||||
ctrlr->dirty = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr));
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg, ch);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "RPMH: Error sending message addr=%#x\n",
|
||||
rpm_msg.msg.cmds[0].addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_req_valid(struct cache_req *req)
|
||||
{
|
||||
return (req->sleep_val != UINT_MAX &&
|
||||
req->wake_val != UINT_MAX &&
|
||||
req->sleep_val != req->wake_val);
|
||||
}
|
||||
|
||||
static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state,
|
||||
u32 addr, u32 data, int ch)
|
||||
{
|
||||
DEFINE_RPMH_MSG_ONSTACK(state, rpm_msg);
|
||||
|
||||
/* Wake sets are always complete and sleep sets are not */
|
||||
rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE);
|
||||
rpm_msg.cmd[0].addr = addr;
|
||||
rpm_msg.cmd[0].data = data;
|
||||
rpm_msg.msg.num_cmds = 1;
|
||||
|
||||
return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg, ch);
|
||||
}
|
||||
|
||||
int rpmh_flush(struct rpmh_ctrlr *ctrlr, int ch)
|
||||
{
|
||||
struct cache_req *p;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!ctrlr->dirty) {
|
||||
printk(BIOS_DEBUG, "RPMH: Skipping flush, TCS has latest data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Invalidate the TCSes first to avoid stale data */
|
||||
rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr), ch);
|
||||
|
||||
/* First flush the cached batch requests */
|
||||
ret = flush_batch(ctrlr, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ctrlr->non_batch_cache_idx; i++) {
|
||||
p = &ctrlr->non_batch_cache[i];
|
||||
if (!is_req_valid(p)) {
|
||||
printk(BIOS_DEBUG, "RPMH: skipping req: a:%#x s:%#x w:%#x\n",
|
||||
p->addr, p->sleep_val, p->wake_val);
|
||||
continue;
|
||||
}
|
||||
ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr,
|
||||
p->sleep_val, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr,
|
||||
p->wake_val, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctrlr->dirty = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rpmh_write_sleep_and_wake(void)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ch, ret;
|
||||
|
||||
if (!ctrlr)
|
||||
return -1;
|
||||
|
||||
ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr));
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
ret = rpmh_flush(ctrlr, ch);
|
||||
if (ret || !(ctrlr->flags & HW_CHANNEL_PRESENT))
|
||||
return ret;
|
||||
|
||||
return rpmh_rsc_switch_channel(ctrlr_to_drv(ctrlr), ch);
|
||||
}
|
||||
|
||||
void rpmh_invalidate(void)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
|
||||
if (rpmh_standalone || !ctrlr)
|
||||
return;
|
||||
|
||||
memset(&ctrlr->batch_cache[RPMH_SLEEP_STATE], 0, sizeof(struct rpmh_request));
|
||||
memset(&ctrlr->batch_cache[RPMH_WAKE_ONLY_STATE], 0, sizeof(struct rpmh_request));
|
||||
ctrlr->dirty = 1;
|
||||
}
|
||||
|
||||
int rpmh_mode_solver_set(bool enable)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
int ret;
|
||||
|
||||
if (rpmh_standalone || !ctrlr)
|
||||
return 0;
|
||||
|
||||
if (!(ctrlr->flags & SOLVER_PRESENT))
|
||||
return -1;
|
||||
|
||||
ret = rpmh_rsc_mode_solver_set(ctrlr_to_drv(ctrlr), enable);
|
||||
if (!ret)
|
||||
ctrlr->in_solver_mode = enable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rpmh_init_fast_path(struct tcs_cmd *cmd, int n)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
struct tcs_request req;
|
||||
int ch;
|
||||
|
||||
if (rpmh_standalone || !ctrlr)
|
||||
return 0;
|
||||
|
||||
ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr));
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
req.cmds = cmd;
|
||||
req.num_cmds = n;
|
||||
req.wait_for_compl = 0;
|
||||
|
||||
return rpmh_rsc_init_fast_path(ctrlr_to_drv(ctrlr), &req, ch);
|
||||
}
|
||||
|
||||
int rpmh_update_fast_path(struct tcs_cmd *cmd, int n, u32 update_mask)
|
||||
{
|
||||
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr();
|
||||
struct tcs_request req;
|
||||
int ch;
|
||||
|
||||
if (rpmh_standalone || !ctrlr)
|
||||
return 0;
|
||||
|
||||
ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr));
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
req.cmds = cmd;
|
||||
req.num_cmds = n;
|
||||
req.wait_for_compl = 0;
|
||||
|
||||
return rpmh_rsc_update_fast_path(ctrlr_to_drv(ctrlr), &req,
|
||||
update_mask, ch);
|
||||
}
|
||||
59
src/soc/qualcomm/common/rpmh_bcm.c
Normal file
59
src/soc/qualcomm/common/rpmh_bcm.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#include <device/device.h>
|
||||
#include <console/console.h>
|
||||
#include <soc/cmd_db.h>
|
||||
#include <soc/rpmh.h>
|
||||
#include <soc/rpmh_bcm.h>
|
||||
#include <soc/tcs.h>
|
||||
|
||||
/**
|
||||
* rpmh_bcm_vote() - Send BCM (Bus Clock Manager) vote to RPMh
|
||||
* @dev: Device pointer for RPMh communication
|
||||
* @resource_name: BCM resource name (e.g., "MM0", "SH0")
|
||||
* @vote_value: Vote value to send
|
||||
*
|
||||
* This function sends a BCM vote to RPMh to enable bandwidth/clock requirements.
|
||||
* It looks up the resource address from Command DB and sends the vote via TCS command.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure
|
||||
*/
|
||||
int rpmh_bcm_vote(const char *resource_name, u32 vote_value)
|
||||
{
|
||||
u32 bcm_addr;
|
||||
struct tcs_cmd cmd;
|
||||
int rc;
|
||||
|
||||
if (!resource_name) {
|
||||
printk(BIOS_ERR, "BCM: Invalid parameters\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the RPMh address for the BCM resource from Command DB */
|
||||
bcm_addr = cmd_db_read_addr(resource_name);
|
||||
if (!bcm_addr) {
|
||||
printk(BIOS_ERR, "BCM: Could not find RPMh address for resource %s\n",
|
||||
resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG, "BCM: Found address 0x%08X for resource %s\n",
|
||||
bcm_addr, resource_name);
|
||||
|
||||
/* Prepare TCS command */
|
||||
cmd.addr = bcm_addr;
|
||||
cmd.data = vote_value;
|
||||
cmd.wait = true;
|
||||
|
||||
/* Send BCM vote via RPMh in active-only state */
|
||||
rc = rpmh_write(RPMH_ACTIVE_ONLY_STATE, &cmd, 1);
|
||||
if (rc) {
|
||||
printk(BIOS_ERR, "BCM: Failed to send vote for %s (addr=0x%08X, val=0x%08X): %d\n",
|
||||
resource_name, bcm_addr, vote_value, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
printk(BIOS_INFO, "BCM: Successfully voted for %s (addr=0x%08X, val=0x%08X)\n",
|
||||
resource_name, bcm_addr, vote_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
458
src/soc/qualcomm/common/rpmh_regulator.c
Normal file
458
src/soc/qualcomm/common/rpmh_regulator.c
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <delay.h>
|
||||
#include <device/device.h>
|
||||
#include <string.h>
|
||||
#include <types.h>
|
||||
#include <soc/rpmh.h>
|
||||
#include <soc/cmd_db.h>
|
||||
#include <soc/rpmh_regulator.h>
|
||||
|
||||
/**
|
||||
* rpmh_regulator_load_arc_level_mapping() - load the RPMh ARC resource's
|
||||
* voltage level mapping from command db
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
static int rpmh_regulator_load_arc_level_mapping(struct rpmh_vreg *vreg)
|
||||
{
|
||||
size_t len = 0;
|
||||
int i, j;
|
||||
const u8 *buf;
|
||||
|
||||
buf = cmd_db_read_aux_data(vreg->resource_name, &len);
|
||||
if (!buf) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Could not retrieve ARC aux data for %s\n",
|
||||
vreg->resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
printk(BIOS_ERR, "RPMH_REG: ARC level mapping data missing for %s\n",
|
||||
vreg->resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len > RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Too many ARC levels defined for %s: %zu > %d\n",
|
||||
vreg->resource_name, len, RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len % RPMH_ARC_LEVEL_SIZE) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Invalid ARC aux data size for %s: %zu\n",
|
||||
vreg->resource_name, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vreg->level_count = len / RPMH_ARC_LEVEL_SIZE;
|
||||
|
||||
for (i = 0; i < vreg->level_count; i++) {
|
||||
vreg->level[i] = 0;
|
||||
for (j = 0; j < RPMH_ARC_LEVEL_SIZE; j++)
|
||||
vreg->level[i] |= buf[i * RPMH_ARC_LEVEL_SIZE + j] << (8 * j);
|
||||
|
||||
/* The AUX data may be zero padded - ignore 0 valued entries at the end */
|
||||
if (i > 0 && vreg->level[i] == 0) {
|
||||
vreg->level_count = i;
|
||||
break;
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG, "RPMH_REG: %s ARC hlvl=%2d --> vlvl=%4u\n",
|
||||
vreg->resource_name, i, vreg->level[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_send_request() - send the RPMh request
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @req: Request to send
|
||||
* @state: RPMh state (active/sleep)
|
||||
* @wait: Whether to wait for completion
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
|
||||
struct rpmh_regulator_request *req,
|
||||
enum rpmh_state state, bool wait)
|
||||
{
|
||||
struct tcs_cmd cmd[RPMH_REGULATOR_REG_MAX];
|
||||
int i, j = 0;
|
||||
int rc;
|
||||
|
||||
/* Build command array from valid registers */
|
||||
for (i = 0; i < RPMH_REGULATOR_REG_MAX; i++) {
|
||||
if (req->valid & BIT(i)) {
|
||||
cmd[j].addr = vreg->addr + i * 4;
|
||||
cmd[j].data = req->reg[i];
|
||||
cmd[j].wait = true;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (j == 0)
|
||||
return 0;
|
||||
|
||||
/* Send the request */
|
||||
if (wait)
|
||||
rc = rpmh_write(state, cmd, j);
|
||||
else
|
||||
rc = rpmh_write_async(state, cmd, j);
|
||||
|
||||
if (rc) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Failed to send %s request for %s, rc=%d\n",
|
||||
state == RPMH_ACTIVE_ONLY_STATE ? "active" : "sleep",
|
||||
vreg->resource_name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG, "RPMH_REG: Sent %s request for %s\n",
|
||||
state == RPMH_ACTIVE_ONLY_STATE ? "active" : "sleep",
|
||||
vreg->resource_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_vrm_set_voltage() - set the voltage of a VRM regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @min_uv: Minimum voltage in microvolts
|
||||
* @max_uv: Maximum voltage in microvolts
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_vrm_set_voltage(struct rpmh_vreg *vreg, int min_uv, int max_uv,
|
||||
bool set_active, bool set_sleep)
|
||||
{
|
||||
int mv;
|
||||
int rc = 0;
|
||||
|
||||
if (!vreg || vreg->type != RPMH_REGULATOR_TYPE_VRM)
|
||||
return -1;
|
||||
|
||||
if (min_uv < RPMH_VRM_MIN_UV || max_uv > RPMH_VRM_MAX_UV) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Voltage range %d-%d uV out of bounds for %s\n",
|
||||
min_uv, max_uv, vreg->resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mv = DIV_ROUND_UP(min_uv, 1000);
|
||||
if (mv * 1000 > max_uv) {
|
||||
printk(BIOS_ERR, "RPMH_REG: No set points available in range %d-%d uV for %s\n",
|
||||
min_uv, max_uv, vreg->resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_active) {
|
||||
vreg->req_active.reg[RPMH_REGULATOR_REG_VRM_VOLTAGE] = mv;
|
||||
vreg->req_active.valid |= BIT(RPMH_REGULATOR_REG_VRM_VOLTAGE);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_active,
|
||||
RPMH_ACTIVE_ONLY_STATE, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (set_sleep) {
|
||||
vreg->req_sleep.reg[RPMH_REGULATOR_REG_VRM_VOLTAGE] = mv;
|
||||
vreg->req_sleep.valid |= BIT(RPMH_REGULATOR_REG_VRM_VOLTAGE);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_sleep,
|
||||
RPMH_SLEEP_STATE, false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_vrm_set_mode() - set the mode of a VRM regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @mode: PMIC mode value
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_vrm_set_mode(struct rpmh_vreg *vreg, u32 mode,
|
||||
bool set_active, bool set_sleep)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!vreg || vreg->type != RPMH_REGULATOR_TYPE_VRM)
|
||||
return -1;
|
||||
|
||||
if (mode < RPMH_VRM_MODE_MIN || mode > RPMH_VRM_MODE_MAX) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Mode %u out of range for %s\n",
|
||||
mode, vreg->resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_active) {
|
||||
vreg->req_active.reg[RPMH_REGULATOR_REG_VRM_MODE] = mode;
|
||||
vreg->req_active.valid |= BIT(RPMH_REGULATOR_REG_VRM_MODE);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_active,
|
||||
RPMH_ACTIVE_ONLY_STATE, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (set_sleep) {
|
||||
vreg->req_sleep.reg[RPMH_REGULATOR_REG_VRM_MODE] = mode;
|
||||
vreg->req_sleep.valid |= BIT(RPMH_REGULATOR_REG_VRM_MODE);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_sleep,
|
||||
RPMH_SLEEP_STATE, false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_enable() - enable a regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_enable(struct rpmh_vreg *vreg, bool set_active, bool set_sleep)
|
||||
{
|
||||
int rc = 0;
|
||||
int reg_index;
|
||||
|
||||
if (!vreg)
|
||||
return -1;
|
||||
|
||||
/* Determine the correct enable register based on type */
|
||||
switch (vreg->type) {
|
||||
case RPMH_REGULATOR_TYPE_VRM:
|
||||
case RPMH_REGULATOR_TYPE_ARC:
|
||||
case RPMH_REGULATOR_TYPE_XOB:
|
||||
reg_index = RPMH_REGULATOR_REG_ENABLE;
|
||||
break;
|
||||
case RPMH_REGULATOR_TYPE_PBS:
|
||||
reg_index = RPMH_REGULATOR_REG_PBS_ENABLE;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_active) {
|
||||
vreg->req_active.reg[reg_index] = 1;
|
||||
vreg->req_active.valid |= BIT(reg_index);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_active,
|
||||
RPMH_ACTIVE_ONLY_STATE, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (set_sleep) {
|
||||
vreg->req_sleep.reg[reg_index] = 1;
|
||||
vreg->req_sleep.valid |= BIT(reg_index);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_sleep,
|
||||
RPMH_SLEEP_STATE, false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_disable() - disable a regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_disable(struct rpmh_vreg *vreg, bool set_active, bool set_sleep)
|
||||
{
|
||||
int rc = 0;
|
||||
int reg_index;
|
||||
|
||||
if (!vreg)
|
||||
return -1;
|
||||
|
||||
/* Determine the correct enable register based on type */
|
||||
switch (vreg->type) {
|
||||
case RPMH_REGULATOR_TYPE_VRM:
|
||||
case RPMH_REGULATOR_TYPE_ARC:
|
||||
case RPMH_REGULATOR_TYPE_XOB:
|
||||
reg_index = RPMH_REGULATOR_REG_ENABLE;
|
||||
break;
|
||||
case RPMH_REGULATOR_TYPE_PBS:
|
||||
reg_index = RPMH_REGULATOR_REG_PBS_ENABLE;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_active) {
|
||||
vreg->req_active.reg[reg_index] = 0;
|
||||
vreg->req_active.valid |= BIT(reg_index);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_active,
|
||||
RPMH_ACTIVE_ONLY_STATE, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (set_sleep) {
|
||||
vreg->req_sleep.reg[reg_index] = 0;
|
||||
vreg->req_sleep.valid |= BIT(reg_index);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_sleep,
|
||||
RPMH_SLEEP_STATE, false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_set_level() - set the voltage level of an ARC regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @level: ARC voltage level (0 to level_count-1)
|
||||
* @set_active: Set in active state
|
||||
* @set_sleep: Set in sleep state
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_arc_set_level(struct rpmh_vreg *vreg, u32 level,
|
||||
bool set_active, bool set_sleep)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!vreg || vreg->type != RPMH_REGULATOR_TYPE_ARC)
|
||||
return -1;
|
||||
|
||||
if (level >= vreg->level_count) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Level %u out of range for %s (max %d)\n",
|
||||
level, vreg->resource_name, vreg->level_count - 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_active) {
|
||||
vreg->req_active.reg[RPMH_REGULATOR_REG_ARC_LEVEL] = level;
|
||||
vreg->req_active.valid |= BIT(RPMH_REGULATOR_REG_ARC_LEVEL);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_active,
|
||||
RPMH_ACTIVE_ONLY_STATE, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (set_sleep) {
|
||||
vreg->req_sleep.reg[RPMH_REGULATOR_REG_ARC_LEVEL] = level;
|
||||
vreg->req_sleep.valid |= BIT(RPMH_REGULATOR_REG_ARC_LEVEL);
|
||||
rc = rpmh_regulator_send_request(vreg, &vreg->req_sleep,
|
||||
RPMH_SLEEP_STATE, false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_get_level() - get the current voltage level of an ARC regulator
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
*
|
||||
* This function returns the cached ARC voltage level that was last set.
|
||||
* Note: This returns the requested level, not necessarily the actual hardware level,
|
||||
* as there's no way to read back from RPMh hardware.
|
||||
*
|
||||
* Return: ARC voltage level (0 to level_count-1) on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_arc_get_level(struct rpmh_vreg *vreg)
|
||||
{
|
||||
if (!vreg || vreg->type != RPMH_REGULATOR_TYPE_ARC)
|
||||
return -1;
|
||||
|
||||
/* Return the active set level if valid, otherwise return 0 */
|
||||
if (vreg->req_active.valid & BIT(RPMH_REGULATOR_REG_ARC_LEVEL))
|
||||
return vreg->req_active.reg[RPMH_REGULATOR_REG_ARC_LEVEL];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_arc_get_voltage_level() - get the voltage level value for an ARC level
|
||||
* @vreg: Pointer to the rpmh regulator
|
||||
* @level: ARC level index
|
||||
*
|
||||
* This function returns the actual voltage level value (vlvl) that corresponds
|
||||
* to the given ARC hardware level (hlvl).
|
||||
*
|
||||
* Return: voltage level value on success, 0 on failure
|
||||
*/
|
||||
u32 rpmh_regulator_arc_get_voltage_level(struct rpmh_vreg *vreg, u32 level)
|
||||
{
|
||||
if (!vreg || vreg->type != RPMH_REGULATOR_TYPE_ARC)
|
||||
return 0;
|
||||
|
||||
if (level >= vreg->level_count)
|
||||
return 0;
|
||||
|
||||
return vreg->level[level];
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmh_regulator_init() - initialize an rpmh regulator
|
||||
* @vreg: Pointer to the rpmh regulator structure to initialize
|
||||
* @dev: Device pointer for RPMh communication
|
||||
* @resource_name: Name of the RPMh resource
|
||||
* @type: Type of regulator (VRM, ARC, XOB, PBS)
|
||||
*
|
||||
* Return: 0 on success, -1 on failure
|
||||
*/
|
||||
int rpmh_regulator_init(struct rpmh_vreg *vreg, const char *resource_name,
|
||||
enum rpmh_regulator_type type)
|
||||
{
|
||||
enum cmd_db_hw_type hw_type;
|
||||
int rc;
|
||||
|
||||
if (!vreg || !resource_name)
|
||||
return -1;
|
||||
|
||||
memset(vreg, 0, sizeof(*vreg));
|
||||
|
||||
vreg->resource_name = resource_name;
|
||||
vreg->type = type;
|
||||
|
||||
/* Get the RPMh address for this resource */
|
||||
vreg->addr = cmd_db_read_addr(resource_name);
|
||||
if (!vreg->addr) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Could not find RPMh address for %s\n",
|
||||
resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify the slave ID matches the expected type */
|
||||
hw_type = cmd_db_read_slave_id(resource_name);
|
||||
if (hw_type == CMD_DB_HW_INVALID) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Could not find slave ID for %s\n",
|
||||
resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Validate hardware type matches regulator type */
|
||||
if ((type == RPMH_REGULATOR_TYPE_ARC && hw_type != CMD_DB_HW_ARC) ||
|
||||
(type == RPMH_REGULATOR_TYPE_VRM && hw_type != CMD_DB_HW_VRM) ||
|
||||
(type == RPMH_REGULATOR_TYPE_XOB && hw_type != CMD_DB_HW_XOB) ||
|
||||
(type == RPMH_REGULATOR_TYPE_BCM && hw_type != CMD_DB_HW_BCM) ||
|
||||
(type == RPMH_REGULATOR_TYPE_PBS && hw_type != CMD_DB_HW_PBS)) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Hardware type mismatch for %s\n",
|
||||
resource_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load ARC level mapping if this is an ARC regulator */
|
||||
if (type == RPMH_REGULATOR_TYPE_ARC) {
|
||||
rc = rpmh_regulator_load_arc_level_mapping(vreg);
|
||||
if (rc) {
|
||||
printk(BIOS_ERR, "RPMH_REG: Failed to load ARC level mapping for %s\n",
|
||||
resource_name);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
printk(BIOS_INFO, "RPMH_REG: Initialized %s at addr=0x%05X\n",
|
||||
resource_name, vreg->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
511
src/soc/qualcomm/common/rpmh_rsc.c
Normal file
511
src/soc/qualcomm/common/rpmh_rsc.c
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <delay.h>
|
||||
#include <device/mmio.h>
|
||||
#include <string.h>
|
||||
#include <timer.h>
|
||||
#include <soc/cmd_db.h>
|
||||
#include <soc/tcs.h>
|
||||
#include <soc/rpmh_internal.h>
|
||||
|
||||
#define TCS_AMC_MODE_ENABLE BIT(16)
|
||||
#define TCS_AMC_MODE_TRIGGER BIT(24)
|
||||
|
||||
/* TCS CMD register bit mask */
|
||||
#define CMD_MSGID_LEN 8
|
||||
#define CMD_MSGID_RESP_REQ BIT(8)
|
||||
#define CMD_MSGID_WRITE BIT(16)
|
||||
#define CMD_STATUS_ISSUED BIT(8)
|
||||
#define CMD_STATUS_COMPL BIT(16)
|
||||
|
||||
/* Offsets for DRV channel status register */
|
||||
#define CH0_CHN_BUSY BIT(0)
|
||||
#define CH1_CHN_BUSY BIT(1)
|
||||
#define CH0_WAKE_TCS_STATUS BIT(0)
|
||||
#define CH0_SLEEP_TCS_STATUS BIT(1)
|
||||
#define CH1_WAKE_TCS_STATUS BIT(2)
|
||||
#define CH1_SLEEP_TCS_STATUS BIT(3)
|
||||
#define CH_CLEAR_STATUS BIT(31)
|
||||
|
||||
static inline void *tcs_reg_addr(const struct rsc_drv *drv, int reg, int tcs_id)
|
||||
{
|
||||
if (!drv->tcs_distance)
|
||||
return drv->tcs_base + drv->regs[RSC_DRV_TCS_OFFSET] * tcs_id + reg;
|
||||
|
||||
return drv->tcs_base + drv->tcs_distance * tcs_id + reg;
|
||||
}
|
||||
|
||||
static inline void *tcs_cmd_addr(const struct rsc_drv *drv, int reg, int tcs_id, int cmd_id)
|
||||
{
|
||||
return tcs_reg_addr(drv, reg, tcs_id) + drv->regs[RSC_DRV_CMD_OFFSET] * cmd_id;
|
||||
}
|
||||
|
||||
static u32 read_tcs_cmd(const struct rsc_drv *drv, int reg, int tcs_id, int cmd_id)
|
||||
{
|
||||
return read32(tcs_cmd_addr(drv, reg, tcs_id, cmd_id));
|
||||
}
|
||||
|
||||
static u32 read_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id)
|
||||
{
|
||||
return read32(tcs_reg_addr(drv, reg, tcs_id));
|
||||
}
|
||||
|
||||
static void write_tcs_cmd(const struct rsc_drv *drv, int reg, int tcs_id,
|
||||
int cmd_id, u32 data)
|
||||
{
|
||||
write32(tcs_cmd_addr(drv, reg, tcs_id, cmd_id), data);
|
||||
}
|
||||
|
||||
static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, u32 data)
|
||||
{
|
||||
write32(tcs_reg_addr(drv, reg, tcs_id), data);
|
||||
}
|
||||
|
||||
static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, u32 data)
|
||||
{
|
||||
struct stopwatch sw;
|
||||
|
||||
write32(tcs_reg_addr(drv, reg, tcs_id), data);
|
||||
|
||||
/* Wait until we read back the same value */
|
||||
stopwatch_init_usecs_expire(&sw, USECS_1S);
|
||||
while (read32(tcs_reg_addr(drv, reg, tcs_id)) != data) {
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR, "%s: error writing %#x to %d:%#x\n", drv->name,
|
||||
data, tcs_id, reg);
|
||||
break;
|
||||
}
|
||||
udelay(LOOP_DELAY_US);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcs_invalidate(struct rsc_drv *drv, int type, int ch)
|
||||
{
|
||||
int m;
|
||||
struct tcs_group *tcs = &drv->ch[ch].tcs[type];
|
||||
|
||||
/* Check if already empty */
|
||||
if (tcs->slots[0] == 0)
|
||||
return;
|
||||
|
||||
for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
|
||||
write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], m, 0);
|
||||
write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_WAIT_FOR_CMPL], m, 0);
|
||||
}
|
||||
|
||||
memset(tcs->slots, 0, sizeof(tcs->slots));
|
||||
}
|
||||
|
||||
int rpmh_rsc_get_channel(struct rsc_drv *drv)
|
||||
{
|
||||
int chn_update, chn_busy;
|
||||
|
||||
if (drv->num_channels == 1)
|
||||
return CH0;
|
||||
|
||||
/* Select Unused channel */
|
||||
do {
|
||||
chn_update = read32(drv->base + drv->regs[RSC_DRV_CHN_UPDATE]);
|
||||
chn_busy = read32(drv->base + drv->regs[RSC_DRV_CHN_BUSY]);
|
||||
} while (chn_busy != chn_update);
|
||||
|
||||
if (chn_busy & CH0_CHN_BUSY)
|
||||
return CH1;
|
||||
else if (chn_busy & CH1_CHN_BUSY)
|
||||
return CH0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void rpmh_rsc_invalidate(struct rsc_drv *drv, int ch)
|
||||
{
|
||||
tcs_invalidate(drv, SLEEP_TCS, ch);
|
||||
tcs_invalidate(drv, WAKE_TCS, ch);
|
||||
}
|
||||
|
||||
static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
|
||||
enum rpmh_state state,
|
||||
int ch)
|
||||
{
|
||||
int type;
|
||||
struct tcs_group *tcs;
|
||||
|
||||
switch (state) {
|
||||
case RPMH_ACTIVE_ONLY_STATE:
|
||||
type = ACTIVE_TCS;
|
||||
break;
|
||||
case RPMH_WAKE_ONLY_STATE:
|
||||
type = WAKE_TCS;
|
||||
break;
|
||||
case RPMH_SLEEP_STATE:
|
||||
type = SLEEP_TCS;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tcs = &drv->ch[ch].tcs[type];
|
||||
if (state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs)
|
||||
tcs = &drv->ch[ch].tcs[WAKE_TCS];
|
||||
|
||||
return tcs;
|
||||
}
|
||||
|
||||
static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger)
|
||||
{
|
||||
u32 enable;
|
||||
u32 reg = drv->regs[RSC_DRV_CONTROL];
|
||||
|
||||
enable = read_tcs_reg(drv, reg, tcs_id);
|
||||
enable &= ~TCS_AMC_MODE_TRIGGER;
|
||||
write_tcs_reg_sync(drv, reg, tcs_id, enable);
|
||||
enable &= ~TCS_AMC_MODE_ENABLE;
|
||||
write_tcs_reg_sync(drv, reg, tcs_id, enable);
|
||||
|
||||
if (trigger) {
|
||||
enable = TCS_AMC_MODE_ENABLE;
|
||||
write_tcs_reg_sync(drv, reg, tcs_id, enable);
|
||||
enable |= TCS_AMC_MODE_TRIGGER;
|
||||
write_tcs_reg(drv, reg, tcs_id, enable);
|
||||
}
|
||||
}
|
||||
|
||||
static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
|
||||
const struct tcs_request *msg)
|
||||
{
|
||||
u32 msgid;
|
||||
u32 cmd_msgid = CMD_MSGID_LEN | CMD_MSGID_WRITE;
|
||||
u32 cmd_enable = 0;
|
||||
u32 cmd_complete;
|
||||
struct tcs_cmd *cmd;
|
||||
int i, j;
|
||||
|
||||
cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0;
|
||||
cmd_complete = read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_WAIT_FOR_CMPL], tcs_id);
|
||||
|
||||
for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) {
|
||||
cmd = &msg->cmds[i];
|
||||
cmd_enable |= BIT(j);
|
||||
cmd_complete |= cmd->wait << j;
|
||||
msgid = cmd_msgid;
|
||||
msgid |= cmd->wait ? CMD_MSGID_RESP_REQ : 0;
|
||||
|
||||
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_MSGID], tcs_id, j, msgid);
|
||||
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], tcs_id, j, cmd->addr);
|
||||
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, j, cmd->data);
|
||||
}
|
||||
|
||||
write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_WAIT_FOR_CMPL], tcs_id, cmd_complete);
|
||||
cmd_enable |= read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id);
|
||||
write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, cmd_enable);
|
||||
}
|
||||
|
||||
static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs,
|
||||
const struct tcs_request *msg)
|
||||
{
|
||||
u32 curr_enabled;
|
||||
u32 addr;
|
||||
int j, k;
|
||||
int i = tcs->offset;
|
||||
|
||||
for (; i < tcs->offset + tcs->num_tcs; i++) {
|
||||
if (!(drv->tcs_in_use[i / 32] & BIT(i % 32)))
|
||||
continue;
|
||||
|
||||
curr_enabled = read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i);
|
||||
|
||||
for (j = 0; j < tcs->ncpt; j++) {
|
||||
if (!(curr_enabled & BIT(j)))
|
||||
continue;
|
||||
|
||||
addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, j);
|
||||
for (k = 0; k < msg->num_cmds; k++) {
|
||||
if (cmd_db_match_resource_addr(msg->cmds[k].addr, addr))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_free_tcs(struct tcs_group *tcs)
|
||||
{
|
||||
const struct rsc_drv *drv = tcs->drv;
|
||||
struct stopwatch sw;
|
||||
int i;
|
||||
u32 sts;
|
||||
|
||||
for (i = tcs->offset; i < tcs->offset + tcs->num_tcs; i++) {
|
||||
if (drv->tcs_in_use[i / 32] & BIT(i % 32))
|
||||
continue;
|
||||
|
||||
stopwatch_init_usecs_expire(&sw, USECS_100US);
|
||||
while (1) {
|
||||
sts = read_tcs_reg(drv, drv->regs[RSC_DRV_STATUS], i);
|
||||
if (sts)
|
||||
return i;
|
||||
if (stopwatch_expired(&sw))
|
||||
return -1;
|
||||
udelay(LOOP_DELAY_US);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int claim_tcs_for_req(struct rsc_drv *drv, struct tcs_group *tcs,
|
||||
const struct tcs_request *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = check_for_req_inflight(drv, tcs, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return find_free_tcs(tcs);
|
||||
}
|
||||
|
||||
int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg, int ch)
|
||||
{
|
||||
struct tcs_group *tcs;
|
||||
struct stopwatch sw;
|
||||
int tcs_id;
|
||||
u32 status;
|
||||
|
||||
tcs = get_tcs_for_msg(drv, msg->state, ch);
|
||||
if (!tcs)
|
||||
return -1;
|
||||
|
||||
/* Controller is busy in 'solver' mode */
|
||||
if (drv->in_solver_mode) {
|
||||
printk(BIOS_ERR, "RPMH RSC: Cannot send ACTIVE request in solver mode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for a free tcs with hardware status verification */
|
||||
stopwatch_init_usecs_expire(&sw, USECS_100MS);
|
||||
while (1) {
|
||||
tcs_id = claim_tcs_for_req(drv, tcs, msg);
|
||||
if (tcs_id >= 0) {
|
||||
/* Double-check hardware status register */
|
||||
status = read_tcs_reg(drv, drv->regs[RSC_DRV_STATUS], tcs_id);
|
||||
if (status) { /* 1 = IDLE, 0 = BUSY */
|
||||
break;
|
||||
}
|
||||
/* TCS claimed but hardware still busy, retry */
|
||||
printk(BIOS_DEBUG, "RPMH RSC: TCS %d claimed but HW busy, retrying\n", tcs_id);
|
||||
}
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR, "RPMH RSC: No free TCS available (timeout after 100ms)\n");
|
||||
return -1;
|
||||
}
|
||||
udelay(LOOP_DELAY_10US);
|
||||
}
|
||||
|
||||
tcs->req[tcs_id - tcs->offset] = msg;
|
||||
drv->tcs_in_use[tcs_id / 32] |= BIT(tcs_id % 32);
|
||||
|
||||
write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, 0);
|
||||
write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_WAIT_FOR_CMPL], tcs_id, 0);
|
||||
|
||||
__tcs_buffer_write(drv, tcs_id, 0, msg);
|
||||
__tcs_set_trigger(drv, tcs_id, 1);
|
||||
|
||||
/* Poll for completion if wait_for_compl is set */
|
||||
if (msg->wait_for_compl) {
|
||||
stopwatch_init_usecs_expire(&sw, USECS_10MS);
|
||||
while (1) {
|
||||
u32 sts = read_tcs_reg(drv, drv->regs[RSC_DRV_STATUS], tcs_id);
|
||||
if (sts)
|
||||
break;
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR, "RPMH RSC: TCS %d timeout\n", tcs_id);
|
||||
return -1;
|
||||
}
|
||||
udelay(LOOP_DELAY_US);
|
||||
}
|
||||
}
|
||||
|
||||
drv->tcs_in_use[tcs_id / 32] &= ~BIT(tcs_id % 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
|
||||
int *tcs_id, int *cmd_id)
|
||||
{
|
||||
int slot, offset;
|
||||
int i = 0;
|
||||
int slots_needed = msg->num_cmds;
|
||||
|
||||
/* Find contiguous slots */
|
||||
do {
|
||||
slot = 0;
|
||||
for (i = 0; i < tcs->ncpt * tcs->num_tcs; i++) {
|
||||
if (tcs->slots[i / 32] & BIT(i % 32)) {
|
||||
slot = 0;
|
||||
} else {
|
||||
slot++;
|
||||
if (slot >= slots_needed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot < slots_needed)
|
||||
return -1;
|
||||
|
||||
i = i - slot + 1;
|
||||
} while (i + slots_needed - 1 >= ((i / tcs->ncpt) + 1) * tcs->ncpt);
|
||||
|
||||
/* Mark slots as used */
|
||||
for (slot = i; slot < i + slots_needed; slot++)
|
||||
tcs->slots[slot / 32] |= BIT(slot % 32);
|
||||
|
||||
offset = i / tcs->ncpt;
|
||||
*tcs_id = offset + tcs->offset;
|
||||
*cmd_id = i % tcs->ncpt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg, int ch)
|
||||
{
|
||||
struct tcs_group *tcs;
|
||||
int tcs_id = 0, cmd_id = 0;
|
||||
int ret;
|
||||
|
||||
if (!msg->num_cmds)
|
||||
return 0;
|
||||
|
||||
tcs = get_tcs_for_msg(drv, msg->state, ch);
|
||||
if (!tcs)
|
||||
return -1;
|
||||
|
||||
ret = find_slots(tcs, msg, &tcs_id, &cmd_id);
|
||||
if (!ret)
|
||||
__tcs_buffer_write(drv, tcs_id, cmd_id, msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable)
|
||||
{
|
||||
drv->in_solver_mode = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmh_rsc_switch_channel(struct rsc_drv *drv, int ch)
|
||||
{
|
||||
struct stopwatch sw;
|
||||
u32 sts;
|
||||
u32 mask = (ch == 0) ? CH0_WAKE_TCS_STATUS : CH1_WAKE_TCS_STATUS;
|
||||
|
||||
write32(drv->base + drv->regs[RSC_DRV_CHN_UPDATE], BIT(ch));
|
||||
|
||||
stopwatch_init_usecs_expire(&sw, USECS_100US);
|
||||
while (1) {
|
||||
sts = read32(drv->base + drv->regs[RSC_DRV_CHN_TCS_COMPLETE]);
|
||||
if (sts & mask)
|
||||
break;
|
||||
if (stopwatch_expired(&sw))
|
||||
return -1;
|
||||
udelay(LOOP_DELAY_US);
|
||||
}
|
||||
|
||||
write32(drv->base + drv->regs[RSC_DRV_CHN_TCS_COMPLETE], CH_CLEAR_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmh_rsc_drv_enable(struct rsc_drv *drv, bool enable)
|
||||
{
|
||||
int ret = 0, ch;
|
||||
u32 chn_en;
|
||||
|
||||
chn_en = read32(drv->base + drv->regs[RSC_DRV_CHN_EN]);
|
||||
if (chn_en == enable)
|
||||
return -1;
|
||||
|
||||
if (enable) {
|
||||
ch = 0;
|
||||
ret = rpmh_flush(&drv->client, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
write32(drv->base + drv->regs[RSC_DRV_CHN_EN], enable);
|
||||
ret = rpmh_rsc_switch_channel(drv, ch);
|
||||
} else {
|
||||
ch = rpmh_rsc_get_channel(drv);
|
||||
if (ch < 0)
|
||||
return ch;
|
||||
|
||||
ret = rpmh_flush(&drv->client, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rpmh_rsc_switch_channel(drv, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
write32(drv->base + drv->regs[RSC_DRV_CHN_UPDATE], 0);
|
||||
write32(drv->base + drv->regs[RSC_DRV_CHN_EN], enable);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rpmh_rsc_init_fast_path(struct rsc_drv *drv, const struct tcs_request *msg, int ch)
|
||||
{
|
||||
int tcs_id;
|
||||
|
||||
if (!drv->ch[ch].tcs[FAST_PATH_TCS].num_tcs)
|
||||
return -1;
|
||||
|
||||
tcs_id = drv->ch[ch].tcs[FAST_PATH_TCS].offset;
|
||||
__tcs_buffer_write(drv, tcs_id, 0, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmh_rsc_update_fast_path(struct rsc_drv *drv,
|
||||
const struct tcs_request *msg,
|
||||
u32 mask, int ch)
|
||||
{
|
||||
struct stopwatch sw;
|
||||
int i;
|
||||
u32 sts;
|
||||
int tcs_id;
|
||||
struct tcs_cmd *cmd;
|
||||
|
||||
if (!drv->ch[ch].tcs[FAST_PATH_TCS].num_tcs)
|
||||
return -1;
|
||||
|
||||
tcs_id = drv->ch[ch].tcs[FAST_PATH_TCS].offset;
|
||||
|
||||
/* Ensure the TCS is free before writing */
|
||||
stopwatch_init_usecs_expire(&sw, USECS_5US);
|
||||
while (1) {
|
||||
sts = read_tcs_reg(drv, drv->regs[RSC_DRV_STATUS], tcs_id);
|
||||
if (sts)
|
||||
break;
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR, "Fast-path TCS is too busy\n");
|
||||
return -1;
|
||||
}
|
||||
udelay(LOOP_DELAY_US);
|
||||
}
|
||||
|
||||
/* Update only the data fields */
|
||||
for (i = 0; i < msg->num_cmds; i++) {
|
||||
if (!(mask & BIT(i)))
|
||||
continue;
|
||||
cmd = &msg->cmds[i];
|
||||
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, i, cmd->data);
|
||||
}
|
||||
|
||||
/* Trigger the TCS */
|
||||
__tcs_set_trigger(drv, tcs_id, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ ramstage-y += ../common/spmi.c
|
|||
ramstage-$(CONFIG_PCI) += pcie.c
|
||||
ramstage-y += cpucp_load_reset.c
|
||||
ramstage-y += ../common/cmd_db.c
|
||||
ramstage-y += ../common/rpmh.c ../common/rpmh_bcm.c ../common/rpmh_regulator.c ../common/rpmh_rsc.c
|
||||
|
||||
################################################################################
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue