android_kernel_xiaomi_sm7250/drivers/regulator/rpmh-regulator.c
David Collins 324e487fe0 regulator: add rpmh-regulator driver
Add the rpmh-regulator driver to support PMIC regulator
management via the RPMh hardware block on Qualcomm Technologies,
Inc. SoCs.

rpmh-regulator devices support PMIC regulator management via the
VRM and ARC RPMh accelerators.  The APPS processor communicates
with these hardware blocks via an RSC using command packets.  The
VRM allows changing four parameters for a given regulator: enable
state, output voltage, operating mode, and minimum headroom
voltage.  The ARC allows changing only a single parameter for a
given regulator: its operating level.  This operating level is
fed into CPR which then decides upon a final explicit voltage for
the regulator.

This is a snapshot commit of msm-4.14 as of:
commit 2913cfe33e10 ("Merge "iommu: arm-smmu: wait for writes to
complete before power off"").  Some modifications have been made
to handle rpmh API differences between msm-4.14 and msm-4.19.

Change-Id: I48e9ab5db808036592cfb26042d843a153fbc92c
Signed-off-by: David Collins <collinsd@codeaurora.org>
2019-01-08 14:28:58 -08:00

2012 lines
59 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <soc/qcom/cmd-db.h>
#include <soc/qcom/rpmh.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator-levels.h>
/**
* 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.
*/
enum rpmh_regulator_type {
RPMH_REGULATOR_TYPE_VRM,
RPMH_REGULATOR_TYPE_ARC,
RPMH_REGULATOR_TYPE_XOB,
};
/**
* enum rpmh_regulator_hw_type - supported PMIC regulator hardware types
* This enum defines the specific regulator type along with its PMIC family.
*/
enum rpmh_regulator_hw_type {
RPMH_REGULATOR_HW_TYPE_UNKNOWN,
RPMH_REGULATOR_HW_TYPE_PMIC4_LDO,
RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS,
RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS,
RPMH_REGULATOR_HW_TYPE_PMIC4_BOB,
RPMH_REGULATOR_HW_TYPE_PMIC5_LDO,
RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS,
RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS,
RPMH_REGULATOR_HW_TYPE_PMIC5_BOB,
RPMH_REGULATOR_HW_TYPE_MAX,
};
/**
* 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_VRM_ENABLE: VRM enable voltage voting register index
* %RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE: Place-holder for enable aggregation.
* ARC does not have a specific register
* for enable voting. Instead, ARC level
* 0 corresponds to "disabled" for a given
* ARC regulator resource if supported.
* %RPMH_REGULATOR_REG_XOB_ENABLE: XOB enable voting register index
* %RPMH_REGULATOR_REG_ENABLE: Common enable index used in callback
* functions for both ARC and VRM.
* %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_ARC_REAL_MAX: Upper limit of real existent ARC
* register indices
* %RPMH_REGULATOR_REG_ARC_MAX: Exclusive upper limit of ARC register
* indices
* %RPMH_REGULATOR_REG_XOB_MAX: Exclusive upper limit of XOB register
* indices
* %RPMH_REGULATOR_REG_VRM_MAX: Exclusive upper limit of VRM register
* indices
* %RPMH_REGULATOR_REG_MAX: Combined exclusive upper limit of ARC
* and VRM register indices
*
* Register addresses are calculated as: base_addr + sizeof(u32) * reg_index
*/
enum rpmh_regulator_reg_index {
RPMH_REGULATOR_REG_VRM_VOLTAGE = 0,
RPMH_REGULATOR_REG_ARC_LEVEL = 0,
RPMH_REGULATOR_REG_VRM_ENABLE = 1,
RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE,
RPMH_REGULATOR_REG_XOB_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE,
RPMH_REGULATOR_REG_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE,
RPMH_REGULATOR_REG_VRM_MODE = 2,
RPMH_REGULATOR_REG_VRM_HEADROOM = 3,
RPMH_REGULATOR_REG_ARC_REAL_MAX = 1,
RPMH_REGULATOR_REG_ARC_MAX = 2,
RPMH_REGULATOR_REG_XOB_MAX = 2,
RPMH_REGULATOR_REG_VRM_MAX = 4,
RPMH_REGULATOR_REG_MAX = 4,
};
/*
* This is the number of bytes used for each command DB aux data entry of an
* ARC resource.
*/
#define RPMH_ARC_LEVEL_SIZE 2
/*
* This is the maximum number of voltage levels that may be defined for an ARC
* resource.
*/
#define RPMH_ARC_MAX_LEVELS 16
#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_HEADROOM_MIN_UV 0
#define RPMH_VRM_HEADROOM_MAX_UV 511000
#define RPMH_VRM_MODE_MIN 0
#define RPMH_VRM_MODE_MAX 7
/* XOB voting registers are found in the VRM hardware module */
#define CMD_DB_HW_XOB CMD_DB_HW_VRM
/**
* struct rpmh_regulator_request - rpmh request data
* @reg: Array of RPMh accelerator register values
* @valid: Bitmask identifying which of the register values
* are valid/initialized
*/
struct rpmh_regulator_request {
u32 reg[RPMH_REGULATOR_REG_MAX];
u32 valid;
};
/**
* struct rpmh_regulator_mode - RPMh VRM mode attributes
* @pmic_mode: Raw PMIC mode value written into VRM mode voting
* register (i.e. RPMH_REGULATOR_MODE_*)
* @framework_mode: Regulator framework mode value
* (i.e. REGULATOR_MODE_*)
* @min_load_ua: The minimum load current in microamps which
* would utilize this mode
*
* Software selects the lowest mode for which aggr_load_ua >= min_load_ua.
*/
struct rpmh_regulator_mode {
u32 pmic_mode;
u32 framework_mode;
int min_load_ua;
};
struct rpmh_vreg;
/**
* struct rpmh_aggr_vreg - top level aggregated rpmh regulator resource data
* structure
* @dev: Device pointer to the rpmh aggregated regulator
* device
* @resource_name: Name of rpmh regulator resource which is mapped
* to an RPMh accelerator address via command DB.
* This name must match to one that is defined by
* the bootloader.
* @addr: Base address of the regulator resource within
* an RPMh accelerator
* @lock: Mutex lock used for locking between regulators
* common to a single aggregated resource
* @regulator_type: RPMh accelerator type for this regulator
* resource
* @regulator_hw_type: The regulator hardware type (e.g. LDO or SMPS)
* along with PMIC family (i.e. PMIC4 or PMIC5)
* @level: Mapping from ARC resource specific voltage
* levels (0 to RPMH_ARC_MAX_LEVELS - 1) to common
* consumer voltage levels (i.e.
* RPMH_REGULATOR_LEVEL_*). These values are read
* out of the AUX data found in command DB for a
* given ARC resource.
* @level_count: The number of valid entries in the level array
* @always_wait_for_ack: Boolean flag indicating if a request must always
* wait for an ACK from RPMh before continuing even
* if it corresponds to a strictly lower power
* state (e.g. enabled --> disabled).
* @next_wait_for_ack: Boolean flag indicating that the next request
* sent must wait for an ACK. This is used to
* ensure that the driver waits for the voltage to
* slew down in the case that the requested max_uV
* value is lower than the last requested voltage.
* @sleep_request_sent: Boolean flag indicating that a sleep set request
* has been sent at some point due to it diverging
* from the active set request. After that point,
* the sleep set requests must always be sent for
* a given resource.
* @vreg: Array of rpmh regulator structs representing the
* individual regulators sharing the aggregated
* regulator resource.
* @vreg_count: The number of entries in the vreg array.
* @mode: An array of modes supported by an RPMh VRM
* regulator resource.
* @mode_count: The number of entries in the mode array.
* @aggr_req_active: Aggregated active set RPMh accelerator register
* request
* @aggr_req_sleep: Aggregated sleep set RPMh accelerator register
* request
*/
struct rpmh_aggr_vreg {
struct device *dev;
const char *resource_name;
u32 addr;
struct mutex lock;
enum rpmh_regulator_type regulator_type;
enum rpmh_regulator_hw_type regulator_hw_type;
u32 level[RPMH_ARC_MAX_LEVELS];
int level_count;
bool always_wait_for_ack;
bool next_wait_for_ack;
bool sleep_request_sent;
struct rpmh_vreg *vreg;
int vreg_count;
struct rpmh_regulator_mode *mode;
int mode_count;
struct rpmh_regulator_request aggr_req_active;
struct rpmh_regulator_request aggr_req_sleep;
};
/**
* struct rpmh_vreg - individual rpmh regulator data structure encapsulating a
* regulator framework regulator device and its corresponding
* rpmh request
* @of_node: Device node pointer for the individual rpmh
* regulator
* @name: Name of the regulator
* @rdesc: Regulator descriptor
* @rdev: Regulator device pointer returned by
* devm_regulator_register()
* @aggr_vreg: Pointer to the aggregated rpmh regulator
* resource
* @set_active: Boolean flag indicating that requests made by
* this regulator should take affect in the active
* set
* @set_sleep: Boolean flag indicating that requests made by
* this regulator should take affect in the sleep
* set
* @req: RPMh accelerator register request
* @mode_index: RPMh VRM regulator mode selected by index into
* aggr_vreg->mode
*/
struct rpmh_vreg {
struct device_node *of_node;
struct regulator_desc rdesc;
struct regulator_dev *rdev;
struct rpmh_aggr_vreg *aggr_vreg;
bool set_active;
bool set_sleep;
struct rpmh_regulator_request req;
int mode_index;
};
#define RPMH_REGULATOR_MODE_COUNT 5
#define RPMH_REGULATOR_MODE_PMIC4_LDO_RM 4
#define RPMH_REGULATOR_MODE_PMIC4_LDO_LPM 5
#define RPMH_REGULATOR_MODE_PMIC4_LDO_HPM 7
#define RPMH_REGULATOR_MODE_PMIC4_SMPS_RM 4
#define RPMH_REGULATOR_MODE_PMIC4_SMPS_PFM 5
#define RPMH_REGULATOR_MODE_PMIC4_SMPS_AUTO 6
#define RPMH_REGULATOR_MODE_PMIC4_SMPS_PWM 7
#define RPMH_REGULATOR_MODE_PMIC4_BOB_PASS 0
#define RPMH_REGULATOR_MODE_PMIC4_BOB_PFM 1
#define RPMH_REGULATOR_MODE_PMIC4_BOB_AUTO 2
#define RPMH_REGULATOR_MODE_PMIC4_BOB_PWM 3
#define RPMH_REGULATOR_MODE_PMIC5_LDO_RM 3
#define RPMH_REGULATOR_MODE_PMIC5_LDO_LPM 4
#define RPMH_REGULATOR_MODE_PMIC5_LDO_HPM 7
#define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_RM 3
#define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PFM 4
#define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_AUTO 6
#define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PWM 7
#define RPMH_REGULATOR_MODE_PMIC5_FTSMPS_RM 3
#define RPMH_REGULATOR_MODE_PMIC5_FTSMPS_PWM 7
#define RPMH_REGULATOR_MODE_PMIC5_BOB_PASS 2
#define RPMH_REGULATOR_MODE_PMIC5_BOB_PFM 4
#define RPMH_REGULATOR_MODE_PMIC5_BOB_AUTO 6
#define RPMH_REGULATOR_MODE_PMIC5_BOB_PWM 7
/*
* Mappings from RPMh generic modes to VRM accelerator modes and regulator
* framework modes for each regulator type.
*/
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic4_ldo[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_RET] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_RM,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_LPM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_HPM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic4_smps[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_RET] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_RM,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_PFM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_AUTO] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_AUTO,
.framework_mode = REGULATOR_MODE_NORMAL,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_PWM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic4_bob[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_PASS] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PASS,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PFM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_AUTO] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_AUTO,
.framework_mode = REGULATOR_MODE_NORMAL,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PWM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic5_ldo[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_RET] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_RM,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_LPM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_HPM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic5_hfsmps[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_RET] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_RM,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PFM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_AUTO] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_AUTO,
.framework_mode = REGULATOR_MODE_NORMAL,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PWM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic5_ftsmps[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_RET] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_FTSMPS_RM,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_FTSMPS_PWM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode
rpmh_regulator_mode_map_pmic5_bob[RPMH_REGULATOR_MODE_COUNT] = {
[RPMH_REGULATOR_MODE_PASS] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PASS,
.framework_mode = REGULATOR_MODE_STANDBY,
},
[RPMH_REGULATOR_MODE_LPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PFM,
.framework_mode = REGULATOR_MODE_IDLE,
},
[RPMH_REGULATOR_MODE_AUTO] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_AUTO,
.framework_mode = REGULATOR_MODE_NORMAL,
},
[RPMH_REGULATOR_MODE_HPM] = {
.pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PWM,
.framework_mode = REGULATOR_MODE_FAST,
},
};
static const struct rpmh_regulator_mode * const
rpmh_regulator_mode_map[RPMH_REGULATOR_HW_TYPE_MAX] = {
[RPMH_REGULATOR_HW_TYPE_PMIC4_LDO]
= rpmh_regulator_mode_map_pmic4_ldo,
[RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS]
= rpmh_regulator_mode_map_pmic4_smps,
[RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS]
= rpmh_regulator_mode_map_pmic4_smps,
[RPMH_REGULATOR_HW_TYPE_PMIC4_BOB]
= rpmh_regulator_mode_map_pmic4_bob,
[RPMH_REGULATOR_HW_TYPE_PMIC5_LDO]
= rpmh_regulator_mode_map_pmic5_ldo,
[RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS]
= rpmh_regulator_mode_map_pmic5_hfsmps,
[RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS]
= rpmh_regulator_mode_map_pmic5_ftsmps,
[RPMH_REGULATOR_HW_TYPE_PMIC5_BOB]
= rpmh_regulator_mode_map_pmic5_bob,
};
/*
* This voltage in uV is returned by get_voltage functions when there is no way
* to determine the current voltage level. It is needed because the regulator
* framework treats a 0 uV voltage as an error.
*/
#define VOLTAGE_UNKNOWN 1
#define vreg_err(vreg, message, ...) \
pr_err("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__)
#define vreg_info(vreg, message, ...) \
pr_info("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__)
#define vreg_debug(vreg, message, ...) \
pr_debug("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__)
#define aggr_vreg_err(aggr_vreg, message, ...) \
pr_err("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__)
#define aggr_vreg_info(aggr_vreg, message, ...) \
pr_info("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__)
#define aggr_vreg_debug(aggr_vreg, message, ...) \
pr_debug("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__)
#define DEBUG_PRINT_BUFFER_SIZE 256
static const char *const rpmh_regulator_state_names[] = {
[RPMH_SLEEP_STATE] = "sleep ",
[RPMH_WAKE_ONLY_STATE] = "wake ",
[RPMH_ACTIVE_ONLY_STATE] = "active",
};
static const char *const rpmh_regulator_vrm_param_names[] = {
[RPMH_REGULATOR_REG_VRM_VOLTAGE] = "mv",
[RPMH_REGULATOR_REG_VRM_ENABLE] = "en",
[RPMH_REGULATOR_REG_VRM_MODE] = "mode",
[RPMH_REGULATOR_REG_VRM_HEADROOM] = "hr_mv",
};
static const char *const rpmh_regulator_arc_param_names[] = {
[RPMH_REGULATOR_REG_ARC_LEVEL] = "hlvl",
};
static const char *const rpmh_regulator_xob_param_names[] = {
[RPMH_REGULATOR_REG_XOB_ENABLE] = "en",
};
static const int max_reg_index_map[] = {
[RPMH_REGULATOR_TYPE_VRM] = RPMH_REGULATOR_REG_VRM_MAX,
[RPMH_REGULATOR_TYPE_ARC] = RPMH_REGULATOR_REG_ARC_MAX,
[RPMH_REGULATOR_TYPE_XOB] = RPMH_REGULATOR_REG_XOB_MAX,
};
/**
* rpmh_regulator_get_max_reg_index() - return the number of registers
* associated with the rpmh resource
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
*
* Return: max number of registers for the aggr_vreg rpmh resource
*/
static int rpmh_regulator_get_max_reg_index(struct rpmh_aggr_vreg *aggr_vreg)
{
if (aggr_vreg->regulator_type >= ARRAY_SIZE(max_reg_index_map))
return -EINVAL;
else
return max_reg_index_map[aggr_vreg->regulator_type];
}
/**
* rpmh_regulator_req() - print the rpmh regulator request to the kernel log
* @vreg: Pointer to the RPMh regulator
* @current_req: Pointer to the new request
* @prev_req: Pointer to the last request
* @sent_mask: Bitmask which specifies the parameters sent in this
* request
* @state: The rpmh state that the request was sent for
*
* Return: none
*/
static void rpmh_regulator_req(struct rpmh_vreg *vreg,
struct rpmh_regulator_request *current_req,
struct rpmh_regulator_request *prev_req,
u32 sent_mask,
enum rpmh_state state)
{
struct rpmh_aggr_vreg *aggr_vreg = vreg->aggr_vreg;
char buf[DEBUG_PRINT_BUFFER_SIZE];
size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
const char *const *param_name;
int i, max_reg_index;
int pos = 0;
u32 valid;
bool first;
switch (aggr_vreg->regulator_type) {
case RPMH_REGULATOR_TYPE_VRM:
max_reg_index = RPMH_REGULATOR_REG_VRM_MAX;
param_name = rpmh_regulator_vrm_param_names;
break;
case RPMH_REGULATOR_TYPE_ARC:
max_reg_index = RPMH_REGULATOR_REG_ARC_REAL_MAX;
param_name = rpmh_regulator_arc_param_names;
break;
case RPMH_REGULATOR_TYPE_XOB:
max_reg_index = RPMH_REGULATOR_REG_XOB_MAX;
param_name = rpmh_regulator_xob_param_names;
break;
default:
return;
}
pos += scnprintf(buf + pos, buflen - pos,
"%s (%s), addr=0x%05X: s=%s; sent: ",
aggr_vreg->resource_name, vreg->rdesc.name,
aggr_vreg->addr, rpmh_regulator_state_names[state]);
valid = sent_mask;
first = true;
for (i = 0; i < max_reg_index; i++) {
if (valid & BIT(i)) {
pos += scnprintf(buf + pos, buflen - pos, "%s%s=%u",
(first ? "" : ", "), param_name[i],
current_req->reg[i]);
first = false;
if (aggr_vreg->regulator_type
== RPMH_REGULATOR_TYPE_ARC
&& i == RPMH_REGULATOR_REG_ARC_LEVEL)
pos += scnprintf(buf + pos, buflen - pos,
" (vlvl=%u)",
aggr_vreg->level[current_req->reg[i]]);
}
}
valid = prev_req->valid & ~sent_mask;
if (valid)
pos += scnprintf(buf + pos, buflen - pos, "; prev: ");
first = true;
for (i = 0; i < max_reg_index; i++) {
if (valid & BIT(i)) {
pos += scnprintf(buf + pos, buflen - pos, "%s%s=%u",
(first ? "" : ", "), param_name[i],
current_req->reg[i]);
first = false;
if (aggr_vreg->regulator_type
== RPMH_REGULATOR_TYPE_ARC
&& i == RPMH_REGULATOR_REG_ARC_LEVEL)
pos += scnprintf(buf + pos, buflen - pos,
" (vlvl=%u)",
aggr_vreg->level[current_req->reg[i]]);
}
}
pr_debug("%s\n", buf);
}
/**
* rpmh_regulator_handle_arc_enable() - handle masking of the voltage level
* request based on the pseudo-enable value
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
* @req Pointer to the newly aggregated request
*
* Return: none
*/
static void rpmh_regulator_handle_arc_enable(struct rpmh_aggr_vreg *aggr_vreg,
struct rpmh_regulator_request *req)
{
if (aggr_vreg->regulator_type != RPMH_REGULATOR_TYPE_ARC)
return;
/*
* Mask the voltage level if "off" level is supported and the regulator
* has not been enabled.
*/
if (aggr_vreg->level[0] == RPMH_REGULATOR_LEVEL_OFF) {
if (req->valid & BIT(RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE)) {
if (!req->reg[RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE])
req->reg[RPMH_REGULATOR_REG_ARC_LEVEL] = 0;
} else {
/* Invalidate voltage level if enable is invalid. */
req->valid &= ~BIT(RPMH_REGULATOR_REG_ARC_LEVEL);
}
}
/*
* Mark the pseudo enable bit as invalid so that it is not accidentally
* included in an RPMh command.
*/
req->valid &= ~BIT(RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE);
}
/**
* rpmh_regulator_aggregate_requests() - aggregate the requests from all
* regulators associated with an RPMh resource
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
* @req_active: Pointer to active set request output
* @req_sleep: Pointer to sleep set request output
*
* This function aggregates the requests from the different regulators
* associated with the aggr_vreg resource independently in both the active set
* and sleep set. The aggregated results are stored in req_active and
* req_sleep.
*
* Return: none
*/
static void rpmh_regulator_aggregate_requests(struct rpmh_aggr_vreg *aggr_vreg,
struct rpmh_regulator_request *req_active,
struct rpmh_regulator_request *req_sleep)
{
int i, j, max_reg_index;
max_reg_index = rpmh_regulator_get_max_reg_index(aggr_vreg);
/*
* Perform max aggregration of each register value across all regulators
* which use this RPMh resource.
*/
for (i = 0; i < aggr_vreg->vreg_count; i++) {
if (aggr_vreg->vreg[i].set_active) {
for (j = 0; j < max_reg_index; j++)
req_active->reg[j] = max(req_active->reg[j],
aggr_vreg->vreg[i].req.reg[j]);
req_active->valid |= aggr_vreg->vreg[i].req.valid;
}
if (aggr_vreg->vreg[i].set_sleep) {
for (j = 0; j < max_reg_index; j++)
req_sleep->reg[j] = max(req_sleep->reg[j],
aggr_vreg->vreg[i].req.reg[j]);
req_sleep->valid |= aggr_vreg->vreg[i].req.valid;
}
}
rpmh_regulator_handle_arc_enable(aggr_vreg, req_active);
rpmh_regulator_handle_arc_enable(aggr_vreg, req_sleep);
}
/**
* rpmh_regulator_send_aggregate_requests() - aggregate the requests from all
* regulators associated with an RPMh resource and send the request
* to RPMh
* @vreg: Pointer to the RPMh regulator
*
* This function aggregates the requests from the different regulators
* associated with the aggr_vreg resource independently in both the active set
* and sleep set. The requests are only sent for the sleep set if they differ,
* or have differed in the past, from those of the active set.
*
* Return: 0 on success, errno on failure
*/
static int
rpmh_regulator_send_aggregate_requests(struct rpmh_vreg *vreg)
{
struct rpmh_aggr_vreg *aggr_vreg = vreg->aggr_vreg;
struct rpmh_regulator_request req_active = { {0} };
struct rpmh_regulator_request req_sleep = { {0} };
struct tcs_cmd cmd[RPMH_REGULATOR_REG_MAX] = { {0} };
bool sleep_set_differs = aggr_vreg->sleep_request_sent;
bool wait_for_ack = aggr_vreg->always_wait_for_ack
|| aggr_vreg->next_wait_for_ack;
bool resend_active = false;
int i, j, max_reg_index, rc;
enum rpmh_state state;
u32 sent_mask;
max_reg_index = rpmh_regulator_get_max_reg_index(aggr_vreg);
rpmh_regulator_aggregate_requests(aggr_vreg, &req_active, &req_sleep);
/*
* Check if the aggregated sleep set parameter values differ from the
* aggregated active set parameter values.
*/
if (!aggr_vreg->sleep_request_sent) {
for (i = 0; i < max_reg_index; i++) {
if ((req_active.reg[i] != req_sleep.reg[i])
&& (req_sleep.valid & BIT(i))) {
sleep_set_differs = true;
/*
* Resend full active set request so that
* all parameters are specified in the wake-only
* state request.
*/
resend_active = true;
break;
}
}
}
if (sleep_set_differs) {
/*
* Generate an rpmh command consisting of only those registers
* which have new values or which have never been touched before
* (i.e. those that were previously not valid).
*/
sent_mask = 0;
for (i = 0, j = 0; i < max_reg_index; i++) {
if ((req_sleep.valid & BIT(i))
&& (!(aggr_vreg->aggr_req_sleep.valid & BIT(i))
|| aggr_vreg->aggr_req_sleep.reg[i]
!= req_sleep.reg[i])) {
cmd[j].addr = aggr_vreg->addr + i * 4;
cmd[j].data = req_sleep.reg[i];
j++;
sent_mask |= BIT(i);
}
}
/* Send the rpmh command if any register values differ. */
if (j > 0) {
rc = rpmh_write_async(aggr_vreg->dev,
RPMH_SLEEP_STATE, cmd, j);
if (rc) {
aggr_vreg_err(aggr_vreg, "sleep state rpmh_write_async() failed, rc=%d\n",
rc);
return rc;
}
rpmh_regulator_req(vreg, &req_sleep,
&aggr_vreg->aggr_req_sleep,
sent_mask,
RPMH_SLEEP_STATE);
aggr_vreg->sleep_request_sent = true;
aggr_vreg->aggr_req_sleep = req_sleep;
}
}
/*
* Generate an rpmh command consisting of only those registers
* which have new values or which have never been touched before
* (i.e. those that were previously not valid).
*/
sent_mask = 0;
for (i = 0, j = 0; i < max_reg_index; i++) {
if ((req_active.valid & BIT(i))
&& (!(aggr_vreg->aggr_req_active.valid & BIT(i))
|| aggr_vreg->aggr_req_active.reg[i]
!= req_active.reg[i] || resend_active)) {
cmd[j].addr = aggr_vreg->addr + i * 4;
cmd[j].data = req_active.reg[i];
cmd[j].wait = sleep_set_differs;
j++;
sent_mask |= BIT(i);
/*
* Must wait for ACK from RPMh if power state is
* increasing
*/
if (req_active.reg[i]
> aggr_vreg->aggr_req_active.reg[i])
wait_for_ack = true;
}
}
/* Send the rpmh command if any register values differ. */
if (j > 0) {
if (sleep_set_differs) {
state = RPMH_WAKE_ONLY_STATE;
rc = rpmh_write_async(aggr_vreg->dev, state, cmd, j);
if (rc) {
aggr_vreg_err(aggr_vreg, "%s state rpmh_write_async() failed, rc=%d\n",
rpmh_regulator_state_names[state], rc);
return rc;
}
rpmh_regulator_req(vreg, &req_active,
&aggr_vreg->aggr_req_active, sent_mask, state);
for (i = 0; i < j; i++)
cmd[j].wait = false;
}
state = RPMH_ACTIVE_ONLY_STATE;
if (wait_for_ack)
rc = rpmh_write(aggr_vreg->dev, state, cmd, j);
else
rc = rpmh_write_async(aggr_vreg->dev, state,
cmd, j);
if (rc) {
aggr_vreg_err(aggr_vreg, "%s state rpmh_write() failed, rc=%d\n",
rpmh_regulator_state_names[state], rc);
return rc;
}
rpmh_regulator_req(vreg, &req_active,
&aggr_vreg->aggr_req_active, sent_mask, state);
aggr_vreg->aggr_req_active = req_active;
aggr_vreg->next_wait_for_ack = false;
}
return 0;
}
/**
* rpmh_regulator_set_reg() - set a register value within the request for an
* RPMh regulator and return the previous value
* @vreg: Pointer to the RPMh regulator
* @reg_index: Index of the register value to update
* @value: New register value to set
*
* Return: old register value
*/
static u32 rpmh_regulator_set_reg(struct rpmh_vreg *vreg, int reg_index,
u32 value)
{
u32 old_value;
old_value = vreg->req.reg[reg_index];
vreg->req.reg[reg_index] = value;
vreg->req.valid |= BIT(reg_index);
return old_value;
}
/**
* rpmh_regulator_check_param_max() - sets if the next request must wait for
* an ACK based on the previously sent reg[index] value and the new
* max value
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
* @index: Register index
* @new_max: Newly requested maximum allowed value for the parameter
*
* This function is used to handle the case when a consumer makes a new
* (min_uv, max_uv) range request in which the new max_uv is lower than the
* previously requested min_uv. In this case, the driver must wait for an ACK
* from RPMh to ensure that the voltage has completed reducing to the new min_uv
* value since the consumer cannot operate at the old min_uv value.
*
* Return: none
*/
static void rpmh_regulator_check_param_max(struct rpmh_aggr_vreg *aggr_vreg,
int index, u32 new_max)
{
if ((aggr_vreg->aggr_req_active.valid & BIT(index))
&& aggr_vreg->aggr_req_active.reg[index] > new_max)
aggr_vreg->next_wait_for_ack = true;
}
/**
* rpmh_regulator_is_enabled() - return the enable state of the RPMh
* regulator
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each rpmh-regulator device.
*
* Note that for ARC resources, this value is effectively a flag indicating if
* the requested voltage level is masked or unmasked since "disabled" = voltage
* level 0 (if supported).
*
* Return: true if regulator is enabled, false if regulator is disabled
*/
static int rpmh_regulator_is_enabled(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
return !!vreg->req.reg[RPMH_REGULATOR_REG_ENABLE];
}
/**
* rpmh_regulator_enable() - enable the RPMh regulator
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each rpmh-regulator device.
*
* Note that for ARC devices the enable state is handled via the voltage level
* parameter. Therefore, this enable value effectively masks or unmasks the
* enabled voltage level.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_enable(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
u32 prev_enable;
int rc;
mutex_lock(&vreg->aggr_vreg->lock);
prev_enable
= rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, 1);
rc = rpmh_regulator_send_aggregate_requests(vreg);
if (rc) {
vreg_err(vreg, "enable failed, rc=%d\n", rc);
rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE,
prev_enable);
}
mutex_unlock(&vreg->aggr_vreg->lock);
return rc;
}
/**
* rpmh_regulator_disable() - disable the RPMh regulator
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each rpmh-regulator device.
*
* Note that for ARC devices the enable state is handled via the voltage level
* parameter. Therefore, this enable value effectively masks or unmasks the
* enabled voltage level.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_disable(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
u32 prev_enable;
int rc;
mutex_lock(&vreg->aggr_vreg->lock);
prev_enable
= rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, 0);
rc = rpmh_regulator_send_aggregate_requests(vreg);
if (rc) {
vreg_err(vreg, "disable failed, rc=%d\n", rc);
rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE,
prev_enable);
}
mutex_unlock(&vreg->aggr_vreg->lock);
return rc;
}
/**
* rpmh_regulator_vrm_set_voltage() - set the voltage of the VRM rpmh-regulator
* @rdev: Regulator device pointer for the rpmh-regulator
* @min_uv: New voltage in microvolts to set
* @max_uv: Maximum voltage in microvolts allowed
* @selector: Unused
*
* This function is passed as a callback function into the regulator ops that
* are registered for each VRM rpmh-regulator device.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_vrm_set_voltage(struct regulator_dev *rdev,
int min_uv, int max_uv, unsigned int *selector)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
u32 prev_voltage;
int mv;
int rc = 0;
mv = DIV_ROUND_UP(min_uv, 1000);
if (mv * 1000 > max_uv) {
vreg_err(vreg, "no set points available in range %d-%d uV\n",
min_uv, max_uv);
return -EINVAL;
}
mutex_lock(&vreg->aggr_vreg->lock);
prev_voltage
= rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_VOLTAGE, mv);
rpmh_regulator_check_param_max(vreg->aggr_vreg,
RPMH_REGULATOR_REG_VRM_VOLTAGE, max_uv);
rc = rpmh_regulator_send_aggregate_requests(vreg);
if (rc) {
vreg_err(vreg, "set voltage=%d mV failed, rc=%d\n", mv, rc);
rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_VOLTAGE,
prev_voltage);
}
mutex_unlock(&vreg->aggr_vreg->lock);
return rc;
}
/**
* rpmh_regulator_vrm_get_voltage() - get the voltage of the VRM rpmh-regulator
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each VRM rpmh-regulator device.
*
* Return: regulator voltage in microvolts
*/
static int rpmh_regulator_vrm_get_voltage(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
int uv;
uv = vreg->req.reg[RPMH_REGULATOR_REG_VRM_VOLTAGE] * 1000;
if (uv == 0)
uv = VOLTAGE_UNKNOWN;
return uv;
}
/**
* rpmh_regulator_vrm_set_mode_index() - set the mode of a VRM regulator to the
* mode mapped to mode_index
* @vreg: Pointer to the RPMh regulator
* @mode_index: Index into aggr_vreg->mode[] array
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_vrm_set_mode_index(struct rpmh_vreg *vreg,
int mode_index)
{
u32 prev_mode;
int rc;
mutex_lock(&vreg->aggr_vreg->lock);
prev_mode = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_MODE,
vreg->aggr_vreg->mode[mode_index].pmic_mode);
rc = rpmh_regulator_send_aggregate_requests(vreg);
if (rc) {
vreg_err(vreg, "set mode=%u failed, rc=%d\n",
vreg->req.reg[RPMH_REGULATOR_REG_VRM_MODE],
rc);
rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_MODE,
prev_mode);
} else {
vreg->mode_index = mode_index;
}
mutex_unlock(&vreg->aggr_vreg->lock);
return rc;
}
/**
* rpmh_regulator_vrm_set_mode() - set the mode of the VRM rpmh-regulator
* @rdev: Regulator device pointer for the rpmh-regulator
* @mode: The regulator framework mode to set
*
* This function is passed as a callback function into the regulator ops that
* are registered for each VRM rpmh-regulator device.
*
* This function sets the PMIC mode corresponding to the specified framework
* mode. The set of PMIC modes allowed is defined in device tree for a given
* RPMh regulator resource. The full mapping from generic modes to PMIC modes
* and framework modes is defined in the rpmh_regulator_mode_map[] array. The
* RPMh resource specific mapping is defined in the aggr_vreg->mode[] array.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
int i;
for (i = 0; i < vreg->aggr_vreg->mode_count; i++)
if (vreg->aggr_vreg->mode[i].framework_mode == mode)
break;
if (i >= vreg->aggr_vreg->mode_count) {
vreg_err(vreg, "invalid mode=%u\n", mode);
return -EINVAL;
}
return rpmh_regulator_vrm_set_mode_index(vreg, i);
}
/**
* rpmh_regulator_vrm_get_mode() - get the mode of the VRM rpmh-regulator
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each VRM rpmh-regulator device.
*
* Return: the regulator framework mode of the regulator
*/
static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
return vreg->aggr_vreg->mode[vreg->mode_index].framework_mode;
}
/**
* rpmh_regulator_vrm_set_load() - set the PMIC mode based upon the maximum load
* required from the VRM rpmh-regulator
* @rdev: Regulator device pointer for the rpmh-regulator
* @load_ua: Maximum current required from all consumers in microamps
*
* This function is passed as a callback function into the regulator ops that
* are registered for each VRM rpmh-regulator device.
*
* This function sets the mode of the regulator to that which has the highest
* min support load less than or equal to load_ua. Example:
* mode_count = 3
* mode[].min_load_ua = 0, 100000, 6000000
*
* load_ua = 10000 --> mode_index = 0
* load_ua = 250000 --> mode_index = 1
* load_ua = 7000000 --> mode_index = 2
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_ua)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
int i;
/* No need to check element 0 as it will be the default. */
for (i = vreg->aggr_vreg->mode_count - 1; i > 0; i--)
if (vreg->aggr_vreg->mode[i].min_load_ua <= load_ua)
break;
return rpmh_regulator_vrm_set_mode_index(vreg, i);
}
/**
* rpmh_regulator_arc_set_voltage_sel() - set the voltage level of the ARC
* rpmh-regulator device
* @rdev: Regulator device pointer for the rpmh-regulator
* @selector: ARC voltage level to set
*
* This function is passed as a callback function into the regulator ops that
* are registered for each ARC rpmh-regulator device.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_arc_set_voltage_sel(struct regulator_dev *rdev,
unsigned int selector)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
u32 prev_level;
int rc;
mutex_lock(&vreg->aggr_vreg->lock);
prev_level = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ARC_LEVEL,
selector);
rc = rpmh_regulator_send_aggregate_requests(vreg);
if (rc) {
vreg_err(vreg, "set level=%d failed, rc=%d\n",
vreg->req.reg[RPMH_REGULATOR_REG_ARC_LEVEL],
rc);
rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ARC_LEVEL,
prev_level);
}
mutex_unlock(&vreg->aggr_vreg->lock);
return rc;
}
/**
* rpmh_regulator_arc_get_voltage_sel() - get the voltage level of the ARC
* rpmh-regulator device
* @rdev: Regulator device pointer for the rpmh-regulator
*
* This function is passed as a callback function into the regulator ops that
* are registered for each ARC rpmh-regulator device.
*
* Return: ARC voltage level
*/
static int rpmh_regulator_arc_get_voltage_sel(struct regulator_dev *rdev)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
return vreg->req.reg[RPMH_REGULATOR_REG_ARC_LEVEL];
}
/**
* rpmh_regulator_arc_list_voltage() - return the consumer voltage level mapped
* to a given ARC voltage level
* @rdev: Regulator device pointer for the rpmh-regulator
* @selector: ARC voltage level
*
* This function is passed as a callback function into the regulator ops that
* are registered for each ARC rpmh-regulator device.
*
* Data ranges:
* ARC voltage level: 0 - 15 (fixed in hardware)
* Consumer voltage level: 1 - 513 (could be expanded to larger values)
*
* Return: consumer voltage level
*/
static int rpmh_regulator_arc_list_voltage(struct regulator_dev *rdev,
unsigned int selector)
{
struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
if (selector >= vreg->aggr_vreg->level_count)
return 0;
return vreg->aggr_vreg->level[selector];
}
static const struct regulator_ops rpmh_regulator_vrm_ops = {
.enable = rpmh_regulator_enable,
.disable = rpmh_regulator_disable,
.is_enabled = rpmh_regulator_is_enabled,
.set_voltage = rpmh_regulator_vrm_set_voltage,
.get_voltage = rpmh_regulator_vrm_get_voltage,
.set_mode = rpmh_regulator_vrm_set_mode,
.get_mode = rpmh_regulator_vrm_get_mode,
.set_load = rpmh_regulator_vrm_set_load,
};
static const struct regulator_ops rpmh_regulator_arc_ops = {
.enable = rpmh_regulator_enable,
.disable = rpmh_regulator_disable,
.is_enabled = rpmh_regulator_is_enabled,
.set_voltage_sel = rpmh_regulator_arc_set_voltage_sel,
.get_voltage_sel = rpmh_regulator_arc_get_voltage_sel,
.list_voltage = rpmh_regulator_arc_list_voltage,
};
static const struct regulator_ops rpmh_regulator_xob_ops = {
.enable = rpmh_regulator_enable,
.disable = rpmh_regulator_disable,
.is_enabled = rpmh_regulator_is_enabled,
};
static const struct regulator_ops *rpmh_regulator_ops[] = {
[RPMH_REGULATOR_TYPE_VRM] = &rpmh_regulator_vrm_ops,
[RPMH_REGULATOR_TYPE_ARC] = &rpmh_regulator_arc_ops,
[RPMH_REGULATOR_TYPE_XOB] = &rpmh_regulator_xob_ops,
};
/**
* rpmh_regulator_load_arc_level_mapping() - load the RPMh ARC resource's
* voltage level mapping from command db
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
*
* The set of supported RPMH_REGULATOR_LEVEL_* voltage levels (0 - ~512) that
* map to ARC operating levels (0 - 15) is defined in aux data per ARC resource
* in the command db SMEM data structure. It is in a u16 array with 1 to 16
* elements. Note that the aux data array may be zero padded at the end for
* data alignment purposes. Such padding entries are invalid and must be
* ignored.
*
* Return: 0 on success, errno on failure
*/
static int
rpmh_regulator_load_arc_level_mapping(struct rpmh_aggr_vreg *aggr_vreg)
{
int i, j, len, rc;
u8 *buf;
len = cmd_db_read_aux_data_len(aggr_vreg->resource_name);
if (len < 0) {
aggr_vreg_err(aggr_vreg, "could not get ARC aux data len, rc=%d\n",
len);
return len;
} else if (len == 0) {
aggr_vreg_err(aggr_vreg, "ARC level mapping data missing in command db\n");
return -EINVAL;
} else if (len > RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE) {
aggr_vreg_err(aggr_vreg, "more ARC levels defined than allowed: %d > %d\n",
len, RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE);
return -EINVAL;
} else if (len % RPMH_ARC_LEVEL_SIZE) {
aggr_vreg_err(aggr_vreg, "invalid ARC aux data size: %d\n",
len);
return -EINVAL;
}
buf = kzalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = cmd_db_read_aux_data(aggr_vreg->resource_name, buf, len);
if (rc < 0) {
aggr_vreg_err(aggr_vreg, "could not retrieve ARC aux data, rc=%d\n",
rc);
goto done;
} else if (rc != len) {
aggr_vreg_err(aggr_vreg, "could not retrieve all ARC aux data, %d != %d\n",
rc, len);
rc = -EINVAL;
goto done;
}
rc = 0;
aggr_vreg->level_count = len / RPMH_ARC_LEVEL_SIZE;
for (i = 0; i < aggr_vreg->level_count; i++) {
for (j = 0; j < RPMH_ARC_LEVEL_SIZE; j++)
aggr_vreg->level[i] |=
buf[i * RPMH_ARC_LEVEL_SIZE + j] << (8 * j);
/*
* The AUX data may be zero padded. These 0 valued entries at
* the end of the map must be ignored.
*/
if (i > 0 && aggr_vreg->level[i] == 0) {
aggr_vreg->level_count = i;
break;
}
aggr_vreg_debug(aggr_vreg, "ARC hlvl=%2d --> vlvl=%4u\n",
i, aggr_vreg->level[i]);
}
done:
kfree(buf);
return rc;
}
/**
* rpmh_regulator_parse_vrm_modes() - parse the supported mode configurations
* for a VRM RPMh resource from device tree
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
*
* This function initializes the mode[] array of aggr_vreg based upon the values
* of optional device tree properties.
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_parse_vrm_modes(struct rpmh_aggr_vreg *aggr_vreg)
{
struct device_node *node = aggr_vreg->dev->of_node;
const char *type = "";
const struct rpmh_regulator_mode *map;
const char *prop;
int i, len, rc;
u32 *buf;
aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_UNKNOWN;
/* qcom,regulator-type is optional */
prop = "qcom,regulator-type";
if (!of_find_property(node, prop, &len))
return 0;
rc = of_property_read_string(node, prop, &type);
if (rc) {
aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n",
prop, rc);
return rc;
}
if (!strcmp(type, "pmic4-ldo")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC4_LDO;
} else if (!strcmp(type, "pmic4-hfsmps")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS;
} else if (!strcmp(type, "pmic4-ftsmps")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS;
} else if (!strcmp(type, "pmic4-bob")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC4_BOB;
} else if (!strcmp(type, "pmic5-ldo")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC5_LDO;
} else if (!strcmp(type, "pmic5-hfsmps")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS;
} else if (!strcmp(type, "pmic5-ftsmps")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS;
} else if (!strcmp(type, "pmic5-bob")) {
aggr_vreg->regulator_hw_type
= RPMH_REGULATOR_HW_TYPE_PMIC5_BOB;
} else {
aggr_vreg_err(aggr_vreg, "unknown %s = %s\n",
prop, type);
return -EINVAL;
}
map = rpmh_regulator_mode_map[aggr_vreg->regulator_hw_type];
/* qcom,supported-modes is optional */
prop = "qcom,supported-modes";
if (!of_find_property(node, prop, &len))
return 0;
len /= sizeof(u32);
aggr_vreg->mode = devm_kcalloc(aggr_vreg->dev, len,
sizeof(*aggr_vreg->mode), GFP_KERNEL);
if (!aggr_vreg->mode)
return -ENOMEM;
aggr_vreg->mode_count = len;
buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = of_property_read_u32_array(node, prop, buf, len);
if (rc) {
aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n",
prop, rc);
goto done;
}
for (i = 0; i < len; i++) {
if (buf[i] >= RPMH_REGULATOR_MODE_COUNT) {
aggr_vreg_err(aggr_vreg, "element %d of %s = %u is invalid\n",
i, prop, buf[i]);
rc = -EINVAL;
goto done;
}
if (!map[buf[i]].framework_mode) {
aggr_vreg_err(aggr_vreg, "element %d of %s = %u is invalid for regulator type = %s\n",
i, prop, buf[i], type);
rc = -EINVAL;
goto done;
}
aggr_vreg->mode[i].pmic_mode = map[buf[i]].pmic_mode;
aggr_vreg->mode[i].framework_mode = map[buf[i]].framework_mode;
if (i > 0 && aggr_vreg->mode[i].pmic_mode
<= aggr_vreg->mode[i - 1].pmic_mode) {
aggr_vreg_err(aggr_vreg, "%s elements are not in ascending order\n",
prop);
rc = -EINVAL;
goto done;
}
}
prop = "qcom,mode-threshold-currents";
rc = of_property_read_u32_array(node, prop, buf, len);
if (rc) {
aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n",
prop, rc);
goto done;
}
for (i = 0; i < len; i++) {
aggr_vreg->mode[i].min_load_ua = buf[i];
if (i > 0 && aggr_vreg->mode[i].min_load_ua
<= aggr_vreg->mode[i - 1].min_load_ua) {
aggr_vreg_err(aggr_vreg, "%s elements are not in ascending order\n",
prop);
rc = -EINVAL;
goto done;
}
}
done:
kfree(buf);
return rc;
}
/**
* rpmh_regulator_allocate_vreg() - allocate space for the regulators associated
* with the RPMh regulator resource and initialize important
* pointers for each regulator
* @aggr_vreg: Pointer to the aggregated rpmh regulator resource
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_allocate_vreg(struct rpmh_aggr_vreg *aggr_vreg)
{
struct device_node *node;
int i, rc;
aggr_vreg->vreg_count = 0;
for_each_available_child_of_node(aggr_vreg->dev->of_node, node) {
/* Skip child nodes handled by other drivers. */
if (of_find_property(node, "compatible", NULL))
continue;
aggr_vreg->vreg_count++;
}
if (aggr_vreg->vreg_count == 0) {
aggr_vreg_err(aggr_vreg, "could not find any regulator subnodes\n");
return -ENODEV;
}
aggr_vreg->vreg = devm_kcalloc(aggr_vreg->dev, aggr_vreg->vreg_count,
sizeof(*aggr_vreg->vreg), GFP_KERNEL);
if (!aggr_vreg->vreg)
return -ENOMEM;
i = 0;
for_each_available_child_of_node(aggr_vreg->dev->of_node, node) {
/* Skip child nodes handled by other drivers. */
if (of_find_property(node, "compatible", NULL))
continue;
aggr_vreg->vreg[i].of_node = node;
aggr_vreg->vreg[i].aggr_vreg = aggr_vreg;
rc = of_property_read_string(node, "regulator-name",
&aggr_vreg->vreg[i].rdesc.name);
if (rc) {
aggr_vreg_err(aggr_vreg, "could not read regulator-name property, rc=%d\n",
rc);
return rc;
}
i++;
}
return 0;
}
/**
* rpmh_regulator_load_default_parameters() - initialize the RPMh resource
* request for this regulator based on optional device tree
* properties
* @vreg: Pointer to the RPMh regulator
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_load_default_parameters(struct rpmh_vreg *vreg)
{
enum rpmh_regulator_type type = vreg->aggr_vreg->regulator_type;
const struct rpmh_regulator_mode *map;
const char *prop;
int i, rc;
u32 temp;
if (type == RPMH_REGULATOR_TYPE_ARC) {
prop = "qcom,init-voltage-level";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc) {
for (i = 0; i < vreg->aggr_vreg->level_count; i++)
if (temp <= vreg->aggr_vreg->level[i])
break;
if (i < vreg->aggr_vreg->level_count) {
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_ARC_LEVEL, i);
} else {
vreg_err(vreg, "%s=%u is invalid\n",
prop, temp);
return -EINVAL;
}
}
prop = "qcom,min-dropout-voltage-level";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc)
vreg->rdesc.min_dropout_uV = temp;
} else if (type == RPMH_REGULATOR_TYPE_VRM) {
prop = "qcom,init-enable";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc)
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_VRM_ENABLE,
!!temp);
prop = "qcom,init-voltage";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc) {
if (temp < RPMH_VRM_MIN_UV || temp > RPMH_VRM_MAX_UV) {
vreg_err(vreg, "%s=%u is invalid\n",
prop, temp);
return -EINVAL;
}
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_VRM_VOLTAGE,
temp / 1000);
}
prop = "qcom,init-mode";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc) {
if (temp >= RPMH_REGULATOR_MODE_COUNT) {
vreg_err(vreg, "%s=%u is invalid\n",
prop, temp);
return -EINVAL;
} else if (vreg->aggr_vreg->regulator_hw_type
== RPMH_REGULATOR_HW_TYPE_UNKNOWN) {
vreg_err(vreg, "qcom,regulator-type missing so %s cannot be used\n",
prop);
return -EINVAL;
}
map = rpmh_regulator_mode_map[
vreg->aggr_vreg->regulator_hw_type];
if (!map[temp].framework_mode) {
vreg_err(vreg, "%s=%u is not supported by type = %d\n",
prop, temp,
vreg->aggr_vreg->regulator_hw_type);
return -EINVAL;
}
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_VRM_MODE,
map[temp].pmic_mode);
for (i = 0; i < vreg->aggr_vreg->mode_count; i++) {
if (vreg->aggr_vreg->mode[i].pmic_mode
== map[temp].pmic_mode) {
vreg->mode_index = i;
break;
}
}
}
prop = "qcom,init-headroom-voltage";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc) {
if (temp < RPMH_VRM_HEADROOM_MIN_UV ||
temp > RPMH_VRM_HEADROOM_MAX_UV) {
vreg_err(vreg, "%s=%u is invalid\n",
prop, temp);
return -EINVAL;
}
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_VRM_HEADROOM,
temp / 1000);
}
prop = "qcom,min-dropout-voltage";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc)
vreg->rdesc.min_dropout_uV = temp;
} else if (type == RPMH_REGULATOR_TYPE_XOB) {
prop = "qcom,init-enable";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
if (!rc)
rpmh_regulator_set_reg(vreg,
RPMH_REGULATOR_REG_XOB_ENABLE,
!!temp);
}
return 0;
}
/**
* rpmh_regulator_init_vreg_supply() - initialize the regulator's parent supply
* mapping based on optional DT parent supply property
* @vreg: Pointer to the RPMh regulator
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_init_vreg_supply(struct rpmh_vreg *vreg)
{
char *buf;
size_t len;
len = strlen(vreg->rdesc.name) + 16;
buf = kzalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
scnprintf(buf, len, "%s-parent-supply", vreg->rdesc.name);
if (of_find_property(vreg->aggr_vreg->dev->of_node, buf, NULL)) {
kfree(buf);
len = strlen(vreg->rdesc.name) + 10;
buf = devm_kzalloc(vreg->aggr_vreg->dev, len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
scnprintf(buf, len, "%s-parent", vreg->rdesc.name);
vreg->rdesc.supply_name = buf;
} else {
kfree(buf);
}
return 0;
}
/**
* rpmh_regulator_init_vreg() - initialize all abbributes of an rpmh-regulator
* @vreg: Pointer to the RPMh regulator
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg)
{
struct device *dev = vreg->aggr_vreg->dev;
enum rpmh_regulator_type type = vreg->aggr_vreg->regulator_type;
struct regulator_config reg_config = {};
struct regulator_init_data *init_data;
struct regulator_ops *ops;
int rc, i;
u32 set;
ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
if (!ops)
return -ENOMEM;
*ops = *rpmh_regulator_ops[type];
vreg->rdesc.owner = THIS_MODULE;
vreg->rdesc.type = REGULATOR_VOLTAGE;
vreg->rdesc.ops = ops;
init_data = of_get_regulator_init_data(dev,
vreg->of_node, &vreg->rdesc);
if (init_data == NULL)
return -ENOMEM;
init_data->constraints.input_uV = init_data->constraints.max_uV;
if (type == RPMH_REGULATOR_TYPE_VRM) {
init_data->constraints.min_uV
= max(init_data->constraints.min_uV, RPMH_VRM_MIN_UV);
init_data->constraints.min_uV
= min(init_data->constraints.min_uV, RPMH_VRM_MAX_UV);
init_data->constraints.max_uV
= max(init_data->constraints.max_uV, RPMH_VRM_MIN_UV);
init_data->constraints.max_uV
= min(init_data->constraints.max_uV, RPMH_VRM_MAX_UV);
}
if (ops->set_voltage || ops->set_voltage_sel)
init_data->constraints.valid_ops_mask
|= REGULATOR_CHANGE_VOLTAGE;
if (type == RPMH_REGULATOR_TYPE_XOB
&& init_data->constraints.min_uV == init_data->constraints.max_uV)
vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
if (vreg->aggr_vreg->mode_count) {
init_data->constraints.valid_ops_mask
|= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
for (i = 0; i < vreg->aggr_vreg->mode_count; i++)
init_data->constraints.valid_modes_mask
|= vreg->aggr_vreg->mode[i].framework_mode;
} else {
ops->get_mode = NULL;
ops->set_mode = NULL;
ops->set_load = NULL;
}
/*
* Remove enable state control if the ARC resource does not support the
* off level.
*/
if (type == RPMH_REGULATOR_TYPE_ARC
&& vreg->aggr_vreg->level[0] != RPMH_REGULATOR_LEVEL_OFF) {
ops->enable = NULL;
ops->disable = NULL;
ops->is_enabled = NULL;
}
if (ops->enable)
init_data->constraints.valid_ops_mask
|= REGULATOR_CHANGE_STATUS;
switch (type) {
case RPMH_REGULATOR_TYPE_VRM:
vreg->rdesc.n_voltages = 2;
break;
case RPMH_REGULATOR_TYPE_ARC:
vreg->rdesc.n_voltages = vreg->aggr_vreg->level_count;
break;
case RPMH_REGULATOR_TYPE_XOB:
vreg->rdesc.n_voltages = 1;
break;
default:
return -EINVAL;
}
rc = of_property_read_u32(vreg->of_node, "qcom,set", &set);
if (rc) {
vreg_err(vreg, "qcom,set property missing, rc=%d\n", rc);
return rc;
} else if (!(set & RPMH_REGULATOR_SET_ALL)) {
vreg_err(vreg, "qcom,set=%u property is invalid\n", set);
return rc;
}
vreg->set_active = !!(set & RPMH_REGULATOR_SET_ACTIVE);
vreg->set_sleep = !!(set & RPMH_REGULATOR_SET_SLEEP);
rc = rpmh_regulator_init_vreg_supply(vreg);
if (rc) {
vreg_err(vreg, "unable to initialize regulator supply name, rc=%d\n",
rc);
return rc;
}
reg_config.dev = dev;
reg_config.init_data = init_data;
reg_config.of_node = vreg->of_node;
reg_config.driver_data = vreg;
rc = rpmh_regulator_load_default_parameters(vreg);
if (rc) {
vreg_err(vreg, "unable to load default parameters, rc=%d\n",
rc);
return rc;
}
vreg->rdev = devm_regulator_register(dev, &vreg->rdesc, &reg_config);
if (IS_ERR(vreg->rdev)) {
rc = PTR_ERR(vreg->rdev);
vreg->rdev = NULL;
vreg_err(vreg, "devm_regulator_register() failed, rc=%d\n", rc);
return rc;
}
vreg_debug(vreg, "successfully registered; set=%s\n",
vreg->set_active && vreg->set_sleep
? "active + sleep"
: vreg->set_active ? "active" : "sleep");
return rc;
}
static const struct of_device_id rpmh_regulator_match_table[] = {
{
.compatible = "qcom,rpmh-vrm-regulator",
.data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_VRM,
},
{
.compatible = "qcom,rpmh-arc-regulator",
.data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_ARC,
},
{
.compatible = "qcom,rpmh-xob-regulator",
.data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_XOB,
},
{}
};
/**
* rpmh_regulator_probe() - probe an aggregated RPMh regulator resource and
* register regulators for each of the regulator nodes associated
* with it
* @pdev: Pointer to the platform device of the aggregated rpmh
* regulator resource
*
* Return: 0 on success, errno on failure
*/
static int rpmh_regulator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct rpmh_aggr_vreg *aggr_vreg;
struct device_node *node;
int rc, i, sid;
node = dev->of_node;
if (!node) {
dev_err(dev, "Device tree node is missing\n");
return -EINVAL;
}
aggr_vreg = devm_kzalloc(dev, sizeof(*aggr_vreg), GFP_KERNEL);
if (!aggr_vreg)
return -ENOMEM;
aggr_vreg->dev = dev;
mutex_init(&aggr_vreg->lock);
match = of_match_node(rpmh_regulator_match_table, node);
if (match) {
aggr_vreg->regulator_type = (uintptr_t)match->data;
} else {
dev_err(dev, "could not find compatible string match\n");
return -ENODEV;
}
rc = of_property_read_string(node, "qcom,resource-name",
&aggr_vreg->resource_name);
if (rc) {
dev_err(dev, "qcom,resource-name missing in DT node\n");
return rc;
}
aggr_vreg->addr = cmd_db_read_addr(aggr_vreg->resource_name);
if (!aggr_vreg->addr) {
aggr_vreg_err(aggr_vreg, "could not find RPMh address for resource\n");
return -ENODEV;
}
sid = cmd_db_read_slave_id(aggr_vreg->resource_name);
if (sid < 0) {
aggr_vreg_err(aggr_vreg, "could not find RPMh slave id for resource, rc=%d\n",
sid);
return sid;
}
/* Confirm slave ID listed in command DB matches DT configuration. */
if ((aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
&& sid != CMD_DB_HW_ARC)
|| (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM
&& sid != CMD_DB_HW_VRM)
|| (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_XOB
&& sid != CMD_DB_HW_XOB)) {
aggr_vreg_err(aggr_vreg, "RPMh slave ID mismatch; config=%d (%s) != cmd-db=%d\n",
aggr_vreg->regulator_type,
aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
? "ARC" : (aggr_vreg->regulator_type
== RPMH_REGULATOR_TYPE_VRM
? "VRM" : "XOB"),
sid);
return -EINVAL;
}
if (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC) {
rc = rpmh_regulator_load_arc_level_mapping(aggr_vreg);
if (rc) {
aggr_vreg_err(aggr_vreg, "could not load arc level mapping, rc=%d\n",
rc);
return rc;
}
} else if (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM) {
rc = rpmh_regulator_parse_vrm_modes(aggr_vreg);
if (rc) {
aggr_vreg_err(aggr_vreg, "could not parse vrm mode mapping, rc=%d\n",
rc);
return rc;
}
}
aggr_vreg->always_wait_for_ack
= of_property_read_bool(node, "qcom,always-wait-for-ack");
rc = rpmh_regulator_allocate_vreg(aggr_vreg);
if (rc) {
aggr_vreg_err(aggr_vreg, "failed to allocate regulator subnode array, rc=%d\n",
rc);
return rc;
}
for (i = 0; i < aggr_vreg->vreg_count; i++) {
rc = rpmh_regulator_init_vreg(&aggr_vreg->vreg[i]);
if (rc) {
pr_err("unable to initialize rpmh-regulator vreg %s for resource %s, rc=%d\n",
aggr_vreg->vreg[i].rdesc.name,
aggr_vreg->resource_name, rc);
return rc;
}
}
if (of_property_read_bool(node, "qcom,send-defaults")) {
mutex_lock(&aggr_vreg->lock);
rc = rpmh_regulator_send_aggregate_requests(
&aggr_vreg->vreg[0]);
if (rc) {
aggr_vreg_err(aggr_vreg, "error while sending default request, rc=%d\n",
rc);
mutex_unlock(&aggr_vreg->lock);
return rc;
}
mutex_unlock(&aggr_vreg->lock);
}
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
platform_set_drvdata(pdev, aggr_vreg);
aggr_vreg_debug(aggr_vreg, "successfully probed; addr=0x%05X, type=%s\n",
aggr_vreg->addr,
aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
? "ARC"
: (aggr_vreg->regulator_type
== RPMH_REGULATOR_TYPE_VRM
? "VRM" : "XOB"));
return rc;
}
static struct platform_driver rpmh_regulator_driver = {
.driver = {
.name = "qcom,rpmh-regulator",
.of_match_table = rpmh_regulator_match_table,
},
.probe = rpmh_regulator_probe,
};
static int rpmh_regulator_init(void)
{
return platform_driver_register(&rpmh_regulator_driver);
}
static void rpmh_regulator_exit(void)
{
platform_driver_unregister(&rpmh_regulator_driver);
}
MODULE_DESCRIPTION("RPMh regulator driver");
MODULE_LICENSE("GPL v2");
arch_initcall(rpmh_regulator_init);
module_exit(rpmh_regulator_exit);