mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-05 02:51:00 +00:00
275 lines
6.1 KiB
C
275 lines
6.1 KiB
C
|
// 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 <regmap.h>
|
||
|
#include <syscon.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 {
|
||
|
struct regmap *regmap;
|
||
|
};
|
||
|
|
||
|
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;
|
||
|
|
||
|
return regmap_write(priv->regmap, STM32MP_PWR_CR3, val);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
return regmap_read(priv->regmap, STM32MP_PWR_CR3, (u32 *)buff);
|
||
|
}
|
||
|
|
||
|
static int stm32mp_pwr_ofdata_to_platdata(struct udevice *dev)
|
||
|
{
|
||
|
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
|
||
|
struct regmap *regmap;
|
||
|
|
||
|
regmap = syscon_get_regmap_by_driver_data(STM32MP_SYSCON_PWR);
|
||
|
if (IS_ERR(regmap)) {
|
||
|
pr_err("%s: unable to find regmap (%ld)\n", __func__,
|
||
|
PTR_ERR(regmap));
|
||
|
return PTR_ERR(regmap);
|
||
|
}
|
||
|
priv->regmap = regmap;
|
||
|
|
||
|
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 *)®, 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 *)®, 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 *)®, 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 *)®, 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,
|
||
|
};
|