This driver binds and manages the following regulator of SoC's PWR block : - reg11 - reg18 - usb33 Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com> Signed-off-by: Patrice Chotard <patrice.chotard@st.com>lime2-spi
parent
cb61c9ec64
commit
e8b85e810a
@ -0,0 +1,274 @@ |
|||||||
|
// 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, |
||||||
|
}; |
Loading…
Reference in new issue