blob: f370d2b44b0b8f9e65f07b134b305c7036500e0a [file] [log] [blame]
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#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.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
/* 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
* @rpmh_client: Handle used for rpmh communications
* @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. Note that the values in
* this array are equal to those read from AUX data
* + RPMH_REGULATOR_LEVEL_OFFSET.
* @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.
* @use_awake_state: Boolean flag indicating that active set requests
* should be made using the awake state instead of
* the active-only state. This should be used for
* RSC's which do not have an AMC.
* @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 rpmh_client *rpmh_client;
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;
bool use_awake_state;
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",
[RPMH_AWAKE_STATE] = "awake ",
};
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",
};
/**
* 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_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;
switch (aggr_vreg->regulator_type) {
case RPMH_REGULATOR_TYPE_VRM:
max_reg_index = RPMH_REGULATOR_REG_VRM_MAX;
break;
case RPMH_REGULATOR_TYPE_ARC:
max_reg_index = RPMH_REGULATOR_REG_ARC_MAX;
break;
case RPMH_REGULATOR_TYPE_XOB:
max_reg_index = RPMH_REGULATOR_REG_XOB_MAX;
break;
default:
return -EINVAL;
}
/*
* 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);
/*
* 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 = !aggr_vreg->use_awake_state;
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->rpmh_client,
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];
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 && !aggr_vreg->use_awake_state) {
state = RPMH_WAKE_ONLY_STATE;
rc = rpmh_write_async(aggr_vreg->rpmh_client, 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);
}
state = aggr_vreg->use_awake_state ? RPMH_AWAKE_STATE
: RPMH_ACTIVE_ONLY_STATE;
if (wait_for_ack)
rc = rpmh_write(aggr_vreg->rpmh_client, state, cmd, j);
else
rc = rpmh_write_async(aggr_vreg->rpmh_client, 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_get_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_get_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;
}
/* Add consumer offset to avoid voltage = 0. */
aggr_vreg->level[i] += RPMH_REGULATOR_LEVEL_OFFSET;
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->use_awake_state = of_property_read_bool(node,
"qcom,use-awake-state");
rc = cmd_db_ready();
if (rc) {
if (rc != -EPROBE_DEFER)
aggr_vreg_err(aggr_vreg, "Command DB not available, rc=%d\n",
rc);
return rc;
}
aggr_vreg->addr = cmd_db_get_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_get_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;
}
aggr_vreg->rpmh_client = rpmh_get_byindex(pdev, 0);
if (IS_ERR(aggr_vreg->rpmh_client)) {
rc = PTR_ERR(aggr_vreg->rpmh_client);
if (rc != -EPROBE_DEFER)
aggr_vreg_err(aggr_vreg, "failed to request RPMh client, rc=%d\n",
rc);
return rc;
}
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);
goto free_client;
}
} 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);
goto free_client;
}
}
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);
goto free_client;
}
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);
goto free_client;
}
}
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);
goto free_client;
}
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;
free_client:
rpmh_release(aggr_vreg->rpmh_client);
return rc;
}
static int rpmh_regulator_remove(struct platform_device *pdev)
{
struct rpmh_aggr_vreg *aggr_vreg = platform_get_drvdata(pdev);
rpmh_release(aggr_vreg->rpmh_client);
return 0;
}
static struct platform_driver rpmh_regulator_driver = {
.driver = {
.name = "qcom,rpmh-regulator",
.of_match_table = rpmh_regulator_match_table,
.owner = THIS_MODULE,
},
.probe = rpmh_regulator_probe,
.remove = rpmh_regulator_remove,
};
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);