regulator: Add snapshot of virtio_regulator driver
Add snapshot for virtio_regulator driver from msm-5.15 at
commit 2993484d0d65 ("drivers: virtio: Remove Backward
compatibility of VIRTIO_ID's").
Change-Id: I17200a54d6aa90862b50108b9d16073b53d68fd1
Signed-off-by: Abdul Salam <quic_asalam@quicinc.com>
This commit is contained in:
parent
fdd94c30ef
commit
9261d6f52b
4 changed files with 681 additions and 0 deletions
|
|
@ -1591,4 +1591,13 @@ config REGULATOR_QTI_OCP_NOTIFIER
|
|||
providing a more graceful recovery mechanism than resetting the entire
|
||||
system.
|
||||
|
||||
config VIRTIO_REGULATOR
|
||||
tristate "Virtio regulator driver"
|
||||
depends on VIRTIO
|
||||
help
|
||||
This is the virtual regulator driver for virtio. It can be used on
|
||||
Qualcomm Technologies Inc. virtual machine. Virtio regulator can vote
|
||||
regulator for pass through peripheral devices via regulator backend in
|
||||
host.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -189,5 +189,6 @@ obj-$(CONFIG_REGULATOR_RPMH) += rpmh-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_STUB) += stub-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DEBUG_CONTROL) += debug-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QTI_OCP_NOTIFIER) += qti-ocp-notifier.o
|
||||
obj-$(CONFIG_VIRTIO_REGULATOR) += virtio_regulator.o
|
||||
|
||||
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
|
||||
|
|
|
|||
637
drivers/regulator/virtio_regulator.c
Normal file
637
drivers/regulator/virtio_regulator.c
Normal file
|
|
@ -0,0 +1,637 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_regulator.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regulator/debug-regulator.h>
|
||||
|
||||
#define VIRTIO_REGULATOR_MAX_NAME 20
|
||||
#define VIRTIO_REGULATOR_VOLTAGE_UNKNOWN 1
|
||||
|
||||
struct reg_virtio;
|
||||
|
||||
struct virtio_regulator {
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
struct completion rsp_avail;
|
||||
struct mutex lock;
|
||||
struct reg_virtio *regs;
|
||||
int regs_count;
|
||||
};
|
||||
|
||||
struct reg_virtio {
|
||||
struct device_node *of_node;
|
||||
struct regulator_desc rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
struct virtio_regulator *vreg;
|
||||
char name[VIRTIO_REGULATOR_MAX_NAME];
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static int virtio_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_ENABLE);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->result);
|
||||
|
||||
if (!ret)
|
||||
reg->enabled = true;
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_DISABLE);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->result);
|
||||
|
||||
if (!ret)
|
||||
reg->enabled = false;
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, reg->enabled);
|
||||
|
||||
return reg->enabled;
|
||||
}
|
||||
|
||||
static int virtio_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
|
||||
int max_uV, unsigned int *selector)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_SET_VOLTAGE);
|
||||
req->data[0] = cpu_to_virtio32(vreg->vdev, DIV_ROUND_UP(min_uV, 1000));
|
||||
req->data[1] = cpu_to_virtio32(vreg->vdev, max_uV / 1000);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->result);
|
||||
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_GET_VOLTAGE);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rsp->result) {
|
||||
pr_debug("%s: error response (%d)\n", reg->rdesc.name,
|
||||
virtio32_to_cpu(vreg->vdev, rsp->result));
|
||||
ret = VIRTIO_REGULATOR_VOLTAGE_UNKNOWN;
|
||||
} else
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->data[0]) * 1000;
|
||||
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_set_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_SET_MODE);
|
||||
req->data[0] = cpu_to_virtio32(vreg->vdev, mode);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->result);
|
||||
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int virtio_regulator_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_GET_MODE);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rsp->result) {
|
||||
pr_err("%s: error response (%d)\n", reg->rdesc.name,
|
||||
virtio32_to_cpu(vreg->vdev, rsp->result));
|
||||
ret = 0;
|
||||
} else
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->data[0]);
|
||||
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_set_load(struct regulator_dev *rdev, int load_ua)
|
||||
{
|
||||
struct reg_virtio *reg = rdev_get_drvdata(rdev);
|
||||
struct virtio_regulator *vreg = reg->vreg;
|
||||
struct virtio_regulator_msg *req, *rsp;
|
||||
struct scatterlist sg[1];
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
req = kzalloc(sizeof(struct virtio_regulator_msg), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpy(req->name, reg->rdesc.name, sizeof(req->name));
|
||||
req->type = cpu_to_virtio32(vreg->vdev, VIRTIO_REGULATOR_T_SET_LOAD);
|
||||
req->data[0] = cpu_to_virtio32(vreg->vdev, load_ua);
|
||||
sg_init_one(sg, req, sizeof(*req));
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
|
||||
ret = virtqueue_add_outbuf(vreg->vq, sg, 1, req, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to add output buffer\n", reg->rdesc.name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
virtqueue_kick(vreg->vq);
|
||||
|
||||
wait_for_completion(&vreg->rsp_avail);
|
||||
|
||||
rsp = virtqueue_get_buf(vreg->vq, &len);
|
||||
if (!rsp) {
|
||||
pr_err("%s: fail to get virtqueue buffer\n", reg->rdesc.name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virtio32_to_cpu(vreg->vdev, rsp->result);
|
||||
|
||||
out:
|
||||
mutex_unlock(&vreg->lock);
|
||||
kfree(req);
|
||||
|
||||
pr_debug("%s return %d\n", reg->rdesc.name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct regulator_ops virtio_regulator_ops = {
|
||||
.enable = virtio_regulator_enable,
|
||||
.disable = virtio_regulator_disable,
|
||||
.is_enabled = virtio_regulator_is_enabled,
|
||||
.set_voltage = virtio_regulator_set_voltage,
|
||||
.get_voltage = virtio_regulator_get_voltage,
|
||||
.set_mode = virtio_regulator_set_mode,
|
||||
.get_mode = virtio_regulator_get_mode,
|
||||
.set_load = virtio_regulator_set_load,
|
||||
};
|
||||
|
||||
static void virtio_regulator_isr(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_regulator *vregulator = vq->vdev->priv;
|
||||
|
||||
complete(&vregulator->rsp_avail);
|
||||
}
|
||||
|
||||
static int virtio_regulator_init_vqs(struct virtio_regulator *vreg)
|
||||
{
|
||||
struct virtqueue *vqs[1];
|
||||
vq_callback_t *cbs[] = { virtio_regulator_isr };
|
||||
static const char * const names[] = { "regulator" };
|
||||
int ret;
|
||||
|
||||
ret = virtio_find_vqs(vreg->vdev, 1, vqs, cbs, names, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vreg->vq = vqs[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_regulator_allocate_reg(struct virtio_regulator *vreg)
|
||||
{
|
||||
struct device_node *parent_node, *node, *child_node;
|
||||
int i, ret;
|
||||
|
||||
vreg->regs_count = 0;
|
||||
parent_node = vreg->vdev->dev.parent->of_node;
|
||||
|
||||
for_each_available_child_of_node(parent_node, node) {
|
||||
for_each_available_child_of_node(node, child_node) {
|
||||
/* Skip child nodes handled by other drivers. */
|
||||
if (of_find_property(child_node, "compatible", NULL))
|
||||
continue;
|
||||
vreg->regs_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (vreg->regs_count == 0) {
|
||||
dev_err(&vreg->vdev->dev,
|
||||
"could not find any regulator subnodes\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vreg->regs = devm_kcalloc(&vreg->vdev->dev, vreg->regs_count,
|
||||
sizeof(*vreg->regs), GFP_KERNEL);
|
||||
if (!vreg->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
for_each_available_child_of_node(parent_node, node) {
|
||||
for_each_available_child_of_node(node, child_node) {
|
||||
/* Skip child nodes handled by other drivers. */
|
||||
if (of_find_property(child_node, "compatible", NULL))
|
||||
continue;
|
||||
|
||||
vreg->regs[i].of_node = child_node;
|
||||
vreg->regs[i].vreg = vreg;
|
||||
|
||||
ret = of_property_read_string(child_node, "regulator-name",
|
||||
&vreg->regs[i].rdesc.name);
|
||||
if (ret) {
|
||||
dev_err(&vreg->vdev->dev,
|
||||
"could not read regulator-name\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_regulator_init_reg(struct reg_virtio *reg)
|
||||
{
|
||||
struct device *dev = reg->vreg->vdev->dev.parent;
|
||||
struct regulator_config reg_config = {};
|
||||
struct regulator_init_data *init_data;
|
||||
int ret = 0;
|
||||
|
||||
reg->rdesc.owner = THIS_MODULE;
|
||||
reg->rdesc.type = REGULATOR_VOLTAGE;
|
||||
reg->rdesc.ops = &virtio_regulator_ops;
|
||||
|
||||
init_data = of_get_regulator_init_data(dev, reg->of_node, ®->rdesc);
|
||||
if (init_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
init_data->constraints.input_uV = init_data->constraints.max_uV;
|
||||
init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
|
||||
|
||||
if (init_data->constraints.min_uV == 0 &&
|
||||
init_data->constraints.max_uV == 0)
|
||||
reg->rdesc.n_voltages = 0;
|
||||
else if (init_data->constraints.min_uV == init_data->constraints.max_uV)
|
||||
reg->rdesc.n_voltages = 1;
|
||||
else
|
||||
reg->rdesc.n_voltages = 2;
|
||||
|
||||
reg_config.dev = dev;
|
||||
reg_config.init_data = init_data;
|
||||
reg_config.of_node = reg->of_node;
|
||||
reg_config.driver_data = reg;
|
||||
|
||||
reg->rdev = devm_regulator_register(dev, ®->rdesc, ®_config);
|
||||
if (IS_ERR(reg->rdev)) {
|
||||
ret = PTR_ERR(reg->rdev);
|
||||
reg->rdev = NULL;
|
||||
dev_err(®->vreg->vdev->dev, "fail to register regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtio_regulator_probe(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_regulator *vreg;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
|
||||
return -ENODEV;
|
||||
|
||||
vreg = devm_kzalloc(&vdev->dev, sizeof(struct virtio_regulator),
|
||||
GFP_KERNEL);
|
||||
if (!vreg)
|
||||
return -ENOMEM;
|
||||
|
||||
vdev->priv = vreg;
|
||||
vreg->vdev = vdev;
|
||||
mutex_init(&vreg->lock);
|
||||
init_completion(&vreg->rsp_avail);
|
||||
|
||||
ret = virtio_regulator_init_vqs(vreg);
|
||||
if (ret) {
|
||||
dev_err(&vdev->dev, "fail to initialize virtqueue\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtio_device_ready(vdev);
|
||||
|
||||
ret = virtio_regulator_allocate_reg(vreg);
|
||||
if (ret) {
|
||||
dev_err(&vdev->dev, "fail to allocate regulators\n");
|
||||
goto err_allocate_reg;
|
||||
}
|
||||
|
||||
for (i = 0; i < vreg->regs_count; i++) {
|
||||
ret = virtio_regulator_init_reg(&vreg->regs[i]);
|
||||
if (ret) {
|
||||
dev_err(&vdev->dev, "fail to initialize regulator %s\n",
|
||||
vreg->regs[i].rdesc.name);
|
||||
goto err_init_reg;
|
||||
}
|
||||
|
||||
ret = devm_regulator_debug_register(&vdev->dev, vreg->regs[i].rdev);
|
||||
if (ret) {
|
||||
dev_err(&vdev->dev, "fail to register regulator %s debugfs\n",
|
||||
vreg->regs[i].rdesc.name);
|
||||
goto err_register_reg_debug;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&vdev->dev, "virtio regulator probe successfully\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_register_reg_debug:
|
||||
err_init_reg:
|
||||
err_allocate_reg:
|
||||
vdev->config->del_vqs(vdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_regulator_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_regulator *vreg = vdev->priv;
|
||||
void *buf;
|
||||
|
||||
vdev->config->reset(vdev);
|
||||
while ((buf = virtqueue_detach_unused_buf(vreg->vq)) != NULL)
|
||||
kfree(buf);
|
||||
vdev->config->del_vqs(vdev);
|
||||
}
|
||||
|
||||
static const struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_REGULATOR, VIRTIO_DEV_ANY_ID },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static unsigned int features[] = {
|
||||
};
|
||||
|
||||
static struct virtio_driver virtio_regulator_driver = {
|
||||
.feature_table = features,
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
.probe = virtio_regulator_probe,
|
||||
.remove = virtio_regulator_remove,
|
||||
};
|
||||
|
||||
static int __init virtio_regulator_init(void)
|
||||
{
|
||||
return register_virtio_driver(&virtio_regulator_driver);
|
||||
}
|
||||
|
||||
static void __exit virtio_regulator_exit(void)
|
||||
{
|
||||
unregister_virtio_driver(&virtio_regulator_driver);
|
||||
}
|
||||
subsys_initcall(virtio_regulator_init);
|
||||
module_exit(virtio_regulator_exit);
|
||||
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
MODULE_DESCRIPTION("Virtio regulator driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
34
include/linux/virtio_regulator.h
Normal file
34
include/linux/virtio_regulator.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _LINUX_VIRTIO_REGULATOR_H
|
||||
#define _LINUX_VIRTIO_REGULATOR_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_types.h>
|
||||
|
||||
/* Request/response message format */
|
||||
struct virtio_regulator_msg {
|
||||
u8 name[20];
|
||||
__virtio32 type;
|
||||
__virtio32 result;
|
||||
__virtio32 data[4];
|
||||
};
|
||||
|
||||
/* Virtio ID of regulator : 0xC001 */
|
||||
#define VIRTIO_ID_REGULATOR 49153
|
||||
|
||||
/* Request type */
|
||||
#define VIRTIO_REGULATOR_T_ENABLE 0
|
||||
#define VIRTIO_REGULATOR_T_DISABLE 1
|
||||
#define VIRTIO_REGULATOR_T_SET_VOLTAGE 2
|
||||
#define VIRTIO_REGULATOR_T_GET_VOLTAGE 3
|
||||
#define VIRTIO_REGULATOR_T_SET_CURRENT_LIMIT 4
|
||||
#define VIRTIO_REGULATOR_T_GET_CURRENT_LIMIT 5
|
||||
#define VIRTIO_REGULATOR_T_SET_MODE 6
|
||||
#define VIRTIO_REGULATOR_T_GET_MODE 7
|
||||
#define VIRTIO_REGULATOR_T_SET_LOAD 8
|
||||
|
||||
#endif /* _LINUX_VIRTIO_REGULATOR_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue