blob: 900dee4c38a6cd81e47377480b02fd92edb43cf2 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <syscon.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <power/pmic.h>
#include <power/regulator.h>
#define STM32MP_PWR_CR3 0xc
#define STM32MP_PWR_CR3_USB33DEN BIT(24)
#define STM32MP_PWR_CR3_USB33RDY BIT(26)
#define STM32MP_PWR_CR3_REG18DEN BIT(28)
#define STM32MP_PWR_CR3_REG18RDY BIT(29)
#define STM32MP_PWR_CR3_REG11DEN BIT(30)
#define STM32MP_PWR_CR3_REG11RDY BIT(31)
struct stm32mp_pwr_reg_info {
u32 enable;
u32 ready;
char *name;
};
struct stm32mp_pwr_priv {
fdt_addr_t base;
};
static int stm32mp_pwr_write(struct udevice *dev, uint reg,
const uint8_t *buff, int len)
{
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
u32 val = *(u32 *)buff;
if (len != 4)
return -EINVAL;
writel(val, priv->base + STM32MP_PWR_CR3);
return 0;
}
static int stm32mp_pwr_read(struct udevice *dev, uint reg, uint8_t *buff,
int len)
{
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
if (len != 4)
return -EINVAL;
*(u32 *)buff = readl(priv->base + STM32MP_PWR_CR3);
return 0;
}
static int stm32mp_pwr_ofdata_to_platdata(struct udevice *dev)
{
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr(dev);
if (priv->base == FDT_ADDR_T_NONE)
return -EINVAL;
return 0;
}
static const struct pmic_child_info pwr_children_info[] = {
{ .prefix = "reg", .driver = "stm32mp_pwr_regulator"},
{ .prefix = "usb", .driver = "stm32mp_pwr_regulator"},
{ },
};
static int stm32mp_pwr_bind(struct udevice *dev)
{
int children;
children = pmic_bind_children(dev, dev->node, pwr_children_info);
if (!children)
dev_dbg(dev, "no child found\n");
return 0;
}
static struct dm_pmic_ops stm32mp_pwr_ops = {
.read = stm32mp_pwr_read,
.write = stm32mp_pwr_write,
};
static const struct udevice_id stm32mp_pwr_ids[] = {
{ .compatible = "st,stm32mp1,pwr-reg" },
{ }
};
U_BOOT_DRIVER(stm32mp_pwr_pmic) = {
.name = "stm32mp_pwr_pmic",
.id = UCLASS_PMIC,
.of_match = stm32mp_pwr_ids,
.bind = stm32mp_pwr_bind,
.ops = &stm32mp_pwr_ops,
.ofdata_to_platdata = stm32mp_pwr_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct stm32mp_pwr_priv),
};
static const struct stm32mp_pwr_reg_info stm32mp_pwr_reg11 = {
.enable = STM32MP_PWR_CR3_REG11DEN,
.ready = STM32MP_PWR_CR3_REG11RDY,
.name = "reg11"
};
static const struct stm32mp_pwr_reg_info stm32mp_pwr_reg18 = {
.enable = STM32MP_PWR_CR3_REG18DEN,
.ready = STM32MP_PWR_CR3_REG18RDY,
.name = "reg18"
};
static const struct stm32mp_pwr_reg_info stm32mp_pwr_usb33 = {
.enable = STM32MP_PWR_CR3_USB33DEN,
.ready = STM32MP_PWR_CR3_USB33RDY,
.name = "usb33"
};
static const struct stm32mp_pwr_reg_info *stm32mp_pwr_reg_infos[] = {
&stm32mp_pwr_reg11,
&stm32mp_pwr_reg18,
&stm32mp_pwr_usb33,
NULL
};
static int stm32mp_pwr_regulator_probe(struct udevice *dev)
{
const struct stm32mp_pwr_reg_info **p = stm32mp_pwr_reg_infos;
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
while (*p) {
int rc;
rc = dev_read_stringlist_search(dev, "regulator-name",
(*p)->name);
if (rc >= 0) {
dev_dbg(dev, "found regulator %s\n", (*p)->name);
break;
} else if (rc != -ENODATA) {
return rc;
}
p++;
}
if (!*p) {
int i = 0;
const char *s;
dev_dbg(dev, "regulator ");
while (dev_read_string_index(dev, "regulator-name",
i++, &s) >= 0)
dev_dbg(dev, "%s'%s' ", (i > 1) ? ", " : "", s);
dev_dbg(dev, "%s not supported\n", (i > 2) ? "are" : "is");
return -EINVAL;
}
uc_pdata->type = REGULATOR_TYPE_FIXED;
dev->priv = (void *)*p;
return 0;
}
static int stm32mp_pwr_regulator_set_value(struct udevice *dev, int uV)
{
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata)
return -ENXIO;
if (uc_pdata->min_uV != uV) {
dev_dbg(dev, "Invalid uV=%d for: %s\n", uV, uc_pdata->name);
return -EINVAL;
}
return 0;
}
static int stm32mp_pwr_regulator_get_value(struct udevice *dev)
{
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata)
return -ENXIO;
if (uc_pdata->min_uV != uc_pdata->max_uV) {
dev_dbg(dev, "Invalid constraints for: %s\n", uc_pdata->name);
return -EINVAL;
}
return uc_pdata->min_uV;
}
static int stm32mp_pwr_regulator_get_enable(struct udevice *dev)
{
const struct stm32mp_pwr_reg_info *p = dev_get_priv(dev);
int rc;
u32 reg;
rc = pmic_read(dev->parent, 0, (uint8_t *)&reg, sizeof(reg));
if (rc)
return rc;
dev_dbg(dev, "%s id %s\n", p->name, (reg & p->enable) ? "on" : "off");
return (reg & p->enable) != 0;
}
static int stm32mp_pwr_regulator_set_enable(struct udevice *dev, bool enable)
{
const struct stm32mp_pwr_reg_info *p = dev_get_priv(dev);
int rc;
u32 reg;
u32 time_start;
dev_dbg(dev, "Turning %s %s\n", enable ? "on" : "off", p->name);
rc = pmic_read(dev->parent, 0, (uint8_t *)&reg, sizeof(reg));
if (rc)
return rc;
/* if regulator is already in the wanted state, nothing to do */
if (!!(reg & p->enable) == enable)
return 0;
reg &= ~p->enable;
if (enable)
reg |= p->enable;
rc = pmic_write(dev->parent, 0, (uint8_t *)&reg, sizeof(reg));
if (rc)
return rc;
if (!enable)
return 0;
/* waiting ready for enable */
time_start = get_timer(0);
while (1) {
rc = pmic_read(dev->parent, 0, (uint8_t *)&reg, sizeof(reg));
if (rc)
return rc;
if (reg & p->ready)
break;
if (get_timer(time_start) > CONFIG_SYS_HZ) {
dev_dbg(dev, "%s: timeout\n", p->name);
return -ETIMEDOUT;
}
}
return 0;
}
static const struct dm_regulator_ops stm32mp_pwr_regulator_ops = {
.set_value = stm32mp_pwr_regulator_set_value,
.get_value = stm32mp_pwr_regulator_get_value,
.get_enable = stm32mp_pwr_regulator_get_enable,
.set_enable = stm32mp_pwr_regulator_set_enable,
};
U_BOOT_DRIVER(stm32mp_pwr_regulator) = {
.name = "stm32mp_pwr_regulator",
.id = UCLASS_REGULATOR,
.ops = &stm32mp_pwr_regulator_ops,
.probe = stm32mp_pwr_regulator_probe,
};