blob: 11a893373390af6a2b7016a53c49a9ab9fc060b2 [file] [log] [blame] [edit]
/*
* pf1550.c - regulator driver for the PF1550
*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Copyright (C) 2017 NXP.
* Robin Gong <yibin.gong@freescale.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/imx_rpmsg.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/rpmsg.h>
#include <linux/uaccess.h>
#include <linux/virtio.h>
#define PF1550_DBGFS
#define PF1550_MAX_REGULATOR 7
#define RPMSG_TIMEOUT 1000
enum pf1550_regs {
PF1550_SW1,
PF1550_SW2,
PF1550_SW3,
PF1550_VREFDDR,
PF1550_LDO1,
PF1550_LDO2,
PF1550_LDO3,
};
enum pf1550_rpmsg_cmd {
PF1550_ENABLE,
PF1550_DISABLE,
PF1550_IS_ENABLED,
PF1550_SET_VOL,
PF1550_GET_VOL,
PF1550_GET_REG,
PF1550_SET_REG,
};
enum pf1550_resp {
PF1550_SUCCESS,
PF1550_FAILED,
PF1550_UNSURPPORT,
};
enum pf1550_status {
PF1550_DISABLED,
PF1550_ENABLED,
};
struct pf1550_regulator_info {
struct rpmsg_device *rpdev;
struct device *dev;
struct pf1550_regulator_rpmsg *msg;
struct completion cmd_complete;
struct pm_qos_request pm_qos_req;
struct mutex lock;
struct regulator_desc *regulators;
};
static struct pf1550_regulator_info pf1550_info;
struct pf1550_regulator_rpmsg {
/* common head */
struct imx_rpmsg_head header;
/* pmic structure */
union {
u8 regulator;
u8 reg;
};
u8 response;
u8 status;
union {
u32 voltage; /* uV */
u32 val;
};
} __attribute__ ((packed));
static int pf1550_send_message(struct pf1550_regulator_rpmsg *msg,
struct pf1550_regulator_info *info)
{
int err;
if (!info->rpdev) {
dev_dbg(info->dev,
"rpmsg channel not ready, m4 image ready?\n");
return -EINVAL;
}
mutex_lock(&info->lock);
pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0);
msg->header.cate = IMX_RPMSG_PMIC;
msg->header.major = IMX_RMPSG_MAJOR;
msg->header.minor = IMX_RMPSG_MINOR;
msg->header.type = 0;
/* wait response from rpmsg */
reinit_completion(&info->cmd_complete);
err = rpmsg_send(info->rpdev->ept, (void *)msg,
sizeof(struct pf1550_regulator_rpmsg));
if (err) {
dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
goto err_out;
}
err = wait_for_completion_timeout(&info->cmd_complete,
msecs_to_jiffies(RPMSG_TIMEOUT));
if (!err) {
dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
err = -ETIMEDOUT;
goto err_out;
}
err = 0;
err_out:
pm_qos_remove_request(&info->pm_qos_req);
mutex_unlock(&info->lock);
dev_dbg(&info->rpdev->dev, "cmd:%d, reg:%d, resp:%d.\n",
msg->header.cmd, msg->regulator, msg->response);
return err;
}
static int pf1550_enable(struct regulator_dev *reg)
{
struct pf1550_regulator_info *info = rdev_get_drvdata(reg);
struct pf1550_regulator_rpmsg msg;
msg.header.cmd = PF1550_ENABLE;
msg.regulator = reg->desc->id;
return pf1550_send_message(&msg, info);
}
static int pf1550_disable(struct regulator_dev *reg)
{
struct pf1550_regulator_info *info = rdev_get_drvdata(reg);
struct pf1550_regulator_rpmsg msg;
msg.header.cmd = PF1550_DISABLE;
msg.regulator = reg->desc->id;
return pf1550_send_message(&msg, info);
}
static int pf1550_is_enabled(struct regulator_dev *reg)
{
struct pf1550_regulator_info *info = rdev_get_drvdata(reg);
struct pf1550_regulator_rpmsg msg;
int err;
msg.header.cmd = PF1550_IS_ENABLED;
msg.regulator = reg->desc->id;
err = pf1550_send_message(&msg, info);
if (err)
return err;
/* Here SUCCESS means ENABLED */
if (info->msg->status == PF1550_ENABLED)
return 1;
else
return 0;
}
static int pf1550_set_voltage(struct regulator_dev *reg,
int minuV, int uV, unsigned *selector)
{
struct pf1550_regulator_info *info = rdev_get_drvdata(reg);
struct pf1550_regulator_rpmsg msg;
int err;
msg.header.cmd = PF1550_SET_VOL;
msg.regulator = reg->desc->id;
msg.voltage = minuV;
err = pf1550_send_message(&msg, info);
if (err)
return err;
if (info->msg->response == PF1550_UNSURPPORT) {
dev_err(info->dev, "Voltages not allowed to set to %d!\n", uV);
return -EINVAL;
}
return 0;
}
static int pf1550_get_voltage(struct regulator_dev *reg)
{
struct pf1550_regulator_info *info = rdev_get_drvdata(reg);
struct pf1550_regulator_rpmsg msg;
int err;
msg.header.cmd = PF1550_GET_VOL;
msg.regulator = reg->desc->id;
msg.voltage = 0;
err = pf1550_send_message(&msg, info);
if (err)
return err;
return info->msg->voltage;
}
/* return the fix voltage */
static int pf1550_get_fix_voltage(struct regulator_dev *dev)
{
return dev->desc->fixed_uV;
}
static struct regulator_ops pf1550_sw_ops = {
.set_voltage = pf1550_set_voltage,
.get_voltage = pf1550_get_voltage,
};
static struct regulator_ops pf1550_ldo_ops = {
.enable = pf1550_enable,
.disable = pf1550_disable,
.is_enabled = pf1550_is_enabled,
.set_voltage = pf1550_set_voltage,
.get_voltage = pf1550_get_voltage,
};
static struct regulator_ops pf1550_fixed_ops = {
.enable = pf1550_enable,
.disable = pf1550_disable,
.get_voltage = pf1550_get_fix_voltage,
.is_enabled = pf1550_is_enabled,
};
static struct regulator_desc pf1550_regulators[PF1550_MAX_REGULATOR] = {
{
.name = "SW1",
.of_match = of_match_ptr("SW1"),
.id = PF1550_SW1,
.ops = &pf1550_sw_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "SW2",
.of_match = of_match_ptr("SW2"),
.id = PF1550_SW2,
.ops = &pf1550_sw_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "SW3",
.of_match = of_match_ptr("SW3"),
.id = PF1550_SW3,
.ops = &pf1550_sw_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "VREFDDR",
.of_match = of_match_ptr("VREFDDR"),
.id = PF1550_VREFDDR,
.ops = &pf1550_fixed_ops,
.type = REGULATOR_VOLTAGE,
.fixed_uV = 1200000,
.owner = THIS_MODULE,
},
{
.name = "LDO1",
.of_match = of_match_ptr("LDO1"),
.id = PF1550_LDO1,
.ops = &pf1550_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "LDO2",
.of_match = of_match_ptr("LDO2"),
.id = PF1550_LDO2,
.ops = &pf1550_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "LDO3",
.of_match = of_match_ptr("LDO3"),
.id = PF1550_LDO3,
.ops = &pf1550_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
};
static int rpmsg_regulator_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
{
struct pf1550_regulator_rpmsg *msg = (struct pf1550_regulator_rpmsg *)data;
dev_dbg(&rpdev->dev, "get from%d: cmd:%d, reg:%d, resp:%d.\n",
src, msg->header.cmd, msg->regulator, msg->response);
pf1550_info.msg = msg;
complete(&pf1550_info.cmd_complete);
return 0;
}
static int rpmsg_regulator_probe(struct rpmsg_device *rpdev)
{
pf1550_info.rpdev = rpdev;
init_completion(&pf1550_info.cmd_complete);
mutex_init(&pf1550_info.lock);
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
rpdev->src, rpdev->dst);
return 0;
}
static void rpmsg_regulator_remove(struct rpmsg_device *rpdev)
{
dev_info(&rpdev->dev, "rpmsg regulator driver is removed\n");
}
static struct rpmsg_device_id rpmsg_regulator_id_table[] = {
{ .name = "rpmsg-regulator-channel" },
{ },
};
static struct rpmsg_driver rpmsg_regulator_driver = {
.drv.name = "regulator_rpmsg",
.drv.owner = THIS_MODULE,
.id_table = rpmsg_regulator_id_table,
.probe = rpmsg_regulator_probe,
.callback = rpmsg_regulator_cb,
.remove = rpmsg_regulator_remove,
};
#ifdef PF1550_DBGFS
#define MAX_REGS 0xff
/*
* Alligned the below two functions as the same as regmap_map_read_file
* and regmap_map_write_file in regmap-debugfs.c
*/
static ssize_t pf1550_registers_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
struct platform_device *pdev = to_platform_device(dev);
struct pf1550_regulator_info *info = platform_get_drvdata(pdev);
struct pf1550_regulator_rpmsg msg;
int err;
size_t bufpos = 0, count = MAX_REGS * 7;
for (i = 0; i < MAX_REGS; i++) {
snprintf(buf + bufpos, count - bufpos, "%.*x: ", 2, i);
bufpos += 4;
msg.header.cmd = PF1550_GET_REG;
msg.reg = i;
msg.val = 0;
err = pf1550_send_message(&msg, info);
if (err)
return err;
if (info->msg->response != PF1550_SUCCESS) {
dev_err(info->dev, "Get register failed %x, resp=%x!\n",
i, info->msg->response);
return -EINVAL;
}
snprintf(buf + bufpos, count - bufpos, "%.*x\n", 2,
info->msg->val);
bufpos += 2;
buf[bufpos++] = '\n';
}
return bufpos;
}
static ssize_t pf1550_register_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct platform_device *pdev = to_platform_device(dev);
struct pf1550_regulator_info *info = platform_get_drvdata(pdev);
struct pf1550_regulator_rpmsg msg;
char *start = (char *)buf;
unsigned long reg, value;
int err;
while (*start == ' ')
start++;
reg = simple_strtoul(start, &start, 16);
while (*start == ' ')
start++;
if (kstrtoul(start, 16, &value))
return -EINVAL;
msg.header.cmd = PF1550_SET_REG;
msg.reg = reg;
msg.val = value;
err = pf1550_send_message(&msg, info);
if (err)
return err;
if (info->msg->response != PF1550_SUCCESS) {
dev_err(info->dev, "set register failed %lx!\n", reg);
return -EINVAL;
}
return size;
}
static DEVICE_ATTR(regs, 0644, pf1550_registers_show, pf1550_register_store);
#endif
static int pf1550_regulator_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int i;
struct regulator_config config = { };
if (!np)
return -ENODEV;
config.dev = &pdev->dev;
config.driver_data = &pf1550_info;
pf1550_info.dev = &pdev->dev;
pf1550_info.regulators = pf1550_regulators;
for (i = 0; i < ARRAY_SIZE(pf1550_regulators); i++) {
struct regulator_dev *rdev;
struct regulator_desc *desc;
desc = &pf1550_info.regulators[i];
rdev = devm_regulator_register(&pdev->dev, desc, &config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev,
"Failed to initialize regulator-%d\n", i);
return PTR_ERR(rdev);
}
}
#ifdef PF1550_DBGFS
i = sysfs_create_file(&config.dev->kobj, &dev_attr_regs.attr);
if (i) {
dev_err(&pdev->dev, "Failed to create pf1550 debug sysfs.\n");
return i;
}
#endif
platform_set_drvdata(pdev, &pf1550_info);
return 0;
}
static const struct of_device_id pf1550_regulator_id[] = {
{"fsl,pf1550-rpmsg",},
{},
};
MODULE_DEVICE_TABLE(of, pf1550_regulator_id);
static struct platform_driver pf1550_regulator_driver = {
.driver = {
.name = "pf1550-rpmsg",
.owner = THIS_MODULE,
.of_match_table = pf1550_regulator_id,
},
.probe = pf1550_regulator_probe,
};
static int __init pf1550_rpmsg_init(void)
{
return register_rpmsg_driver(&rpmsg_regulator_driver);
}
module_platform_driver(pf1550_regulator_driver);
module_init(pf1550_rpmsg_init);
MODULE_DESCRIPTION("Freescale PF1550 regulator rpmsg driver");
MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
MODULE_LICENSE("GPL");