// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, Sony Mobile Communications AB.
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/soc/qcom/smd-rpm.h>
struct qcom_rpm_reg {
struct device *dev;
struct qcom_smd_rpm *rpm;
u32 type;
u32 id;
struct regulator_desc desc;
int is_enabled;
int uV;
u32 load;
unsigned int enabled_updated:1;
unsigned int uv_updated:1;
unsigned int load_updated:1;
};
struct rpm_regulator_req {
__le32 key;
__le32 nbytes;
__le32 value;
};
#define RPM_KEY_SWEN 0x6e657773 /* "swen" */
#define RPM_KEY_UV 0x00007675 /* "uv" */
#define RPM_KEY_MA 0x0000616d /* "ma" */
static int rpm_reg_write_active(struct qcom_rpm_reg *vreg)
{
struct rpm_regulator_req req[3];
int reqlen = 0;
int ret;
if (vreg->enabled_updated) {
req[reqlen].key = cpu_to_le32(RPM_KEY_SWEN);
req[reqlen].nbytes = cpu_to_le32(sizeof(u32));
req[reqlen].value = cpu_to_le32(vreg->is_enabled);
reqlen++;
}
if (vreg->uv_updated && vreg->is_enabled) {
req[reqlen].key = cpu_to_le32(RPM_KEY_UV);
req[reqlen].nbytes = cpu_to_le32(sizeof(u32));
req[reqlen].value = cpu_to_le32(vreg->uV);
reqlen++;
}
if (vreg->load_updated && vreg->is_enabled) {
req[reqlen].key = cpu_to_le32(RPM_KEY_MA);
req[reqlen].nbytes = cpu_to_le32(sizeof(u32));
req[reqlen].value = cpu_to_le32(vreg->load / 1000);
reqlen++;
}
if (!reqlen)
return 0;
ret = qcom_rpm_smd_write(vreg->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
vreg->type, vreg->id,
req, sizeof(req[0]) * reqlen);
if (!ret) {
vreg->enabled_updated = 0;
vreg->uv_updated = 0;
vreg->load_updated = 0;
}
return ret;
}
static int rpm_reg_enable(struct regulator_dev *rdev)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
int ret;
vreg->is_enabled = 1;
vreg->enabled_updated = 1;
ret = rpm_reg_write_active(vreg);
if (ret)
vreg->is_enabled = 0;
return ret;
}
static int rpm_reg_is_enabled(struct regulator_dev *rdev)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
return vreg->is_enabled;
}
static int rpm_reg_disable(struct regulator_dev *rdev)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
int ret;
vreg->is_enabled = 0;
vreg->enabled_updated = 1;
ret = rpm_reg_write_active(vreg);
if (ret)
vreg->is_enabled = 1;
return ret;
}
static int rpm_reg_get_voltage(struct regulator_dev *rdev)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
return vreg->uV;
}
static int rpm_reg_set_voltage(struct regulator_dev *rdev,
int min_uV,
int max_uV,
unsigned *selector)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
int ret;
int old_uV = vreg->uV;
vreg->uV = min_uV;
vreg->uv_updated = 1;
ret = rpm_reg_write_active(vreg);
if (ret)
vreg->uV = old_uV;
return ret;
}
static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA)
{
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
u32 old_load = vreg->load;
int ret;
vreg->load = load_uA;
vreg->l