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