commit
78cb000b84
@ -0,0 +1,301 @@ |
||||
/*
|
||||
* (C) Copyright 2016 Texas Instruments Incorporated, <www.ti.com> |
||||
* Jean-Jacques Hiblot <jjhiblot@ti.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <errno.h> |
||||
#include <dm.h> |
||||
#include <power/pmic.h> |
||||
#include <power/regulator.h> |
||||
#include <regmap.h> |
||||
#include <syscon.h> |
||||
#include <linux/bitops.h> |
||||
#include <linux/ioport.h> |
||||
#include <dm/read.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
struct pbias_reg_info { |
||||
u32 enable; |
||||
u32 enable_mask; |
||||
u32 disable_val; |
||||
u32 vmode; |
||||
unsigned int enable_time; |
||||
char *name; |
||||
}; |
||||
|
||||
struct pbias_priv { |
||||
struct regmap *regmap; |
||||
int offset; |
||||
}; |
||||
|
||||
static const struct pmic_child_info pmic_children_info[] = { |
||||
{ .prefix = "pbias", .driver = "pbias_regulator"}, |
||||
{ }, |
||||
}; |
||||
|
||||
static int pbias_write(struct udevice *dev, uint reg, const uint8_t *buff, |
||||
int len) |
||||
{ |
||||
struct pbias_priv *priv = dev_get_priv(dev); |
||||
u32 val = *(u32 *)buff; |
||||
|
||||
if (len != 4) |
||||
return -EINVAL; |
||||
|
||||
return regmap_write(priv->regmap, priv->offset, val); |
||||
} |
||||
|
||||
static int pbias_read(struct udevice *dev, uint reg, uint8_t *buff, int len) |
||||
{ |
||||
struct pbias_priv *priv = dev_get_priv(dev); |
||||
|
||||
if (len != 4) |
||||
return -EINVAL; |
||||
|
||||
return regmap_read(priv->regmap, priv->offset, (u32 *)buff); |
||||
} |
||||
|
||||
static int pbias_ofdata_to_platdata(struct udevice *dev) |
||||
{ |
||||
struct pbias_priv *priv = dev_get_priv(dev); |
||||
struct udevice *syscon; |
||||
struct regmap *regmap; |
||||
struct resource res; |
||||
int err; |
||||
|
||||
err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, |
||||
"syscon", &syscon); |
||||
if (err) { |
||||
error("%s: unable to find syscon device (%d)\n", __func__, |
||||
err); |
||||
return err; |
||||
} |
||||
|
||||
regmap = syscon_get_regmap(syscon); |
||||
if (IS_ERR(regmap)) { |
||||
error("%s: unable to find regmap (%ld)\n", __func__, |
||||
PTR_ERR(regmap)); |
||||
return PTR_ERR(regmap); |
||||
} |
||||
priv->regmap = regmap; |
||||
|
||||
err = dev_read_resource(dev, 0, &res); |
||||
if (err) { |
||||
error("%s: unable to find offset (%d)\n", __func__, err); |
||||
return err; |
||||
} |
||||
priv->offset = res.start; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int pbias_bind(struct udevice *dev) |
||||
{ |
||||
int children; |
||||
|
||||
children = pmic_bind_children(dev, dev->node, pmic_children_info); |
||||
if (!children) |
||||
debug("%s: %s - no child found\n", __func__, dev->name); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct dm_pmic_ops pbias_ops = { |
||||
.read = pbias_read, |
||||
.write = pbias_write, |
||||
}; |
||||
|
||||
static const struct udevice_id pbias_ids[] = { |
||||
{ .compatible = "ti,pbias-dra7" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(pbias_pmic) = { |
||||
.name = "pbias_pmic", |
||||
.id = UCLASS_PMIC, |
||||
.of_match = pbias_ids, |
||||
.bind = pbias_bind, |
||||
.ops = &pbias_ops, |
||||
.ofdata_to_platdata = pbias_ofdata_to_platdata, |
||||
.priv_auto_alloc_size = sizeof(struct pbias_priv), |
||||
}; |
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap2430 = { |
||||
.enable = BIT(1), |
||||
.enable_mask = BIT(1), |
||||
.vmode = BIT(0), |
||||
.disable_val = 0, |
||||
.enable_time = 100, |
||||
.name = "pbias_mmc_omap2430" |
||||
}; |
||||
|
||||
static const struct pbias_reg_info pbias_sim_omap3 = { |
||||
.enable = BIT(9), |
||||
.enable_mask = BIT(9), |
||||
.vmode = BIT(8), |
||||
.enable_time = 100, |
||||
.name = "pbias_sim_omap3" |
||||
}; |
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap4 = { |
||||
.enable = BIT(26) | BIT(22), |
||||
.enable_mask = BIT(26) | BIT(25) | BIT(22), |
||||
.disable_val = BIT(25), |
||||
.vmode = BIT(21), |
||||
.enable_time = 100, |
||||
.name = "pbias_mmc_omap4" |
||||
}; |
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap5 = { |
||||
.enable = BIT(27) | BIT(26), |
||||
.enable_mask = BIT(27) | BIT(25) | BIT(26), |
||||
.disable_val = BIT(25), |
||||
.vmode = BIT(21), |
||||
.enable_time = 100, |
||||
.name = "pbias_mmc_omap5" |
||||
}; |
||||
|
||||
static const struct pbias_reg_info *pbias_reg_infos[] = { |
||||
&pbias_mmc_omap5, |
||||
&pbias_mmc_omap4, |
||||
&pbias_sim_omap3, |
||||
&pbias_mmc_omap2430, |
||||
NULL |
||||
}; |
||||
|
||||
static int pbias_regulator_probe(struct udevice *dev) |
||||
{ |
||||
const struct pbias_reg_info **p = pbias_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) { |
||||
debug("found regulator %s\n", (*p)->name); |
||||
break; |
||||
} else if (rc != -ENODATA) { |
||||
return rc; |
||||
} |
||||
p++; |
||||
} |
||||
if (!*p) { |
||||
int i = 0; |
||||
const char *s; |
||||
|
||||
debug("regulator "); |
||||
while (dev_read_string_index(dev, "regulator-name", i++, &s) >= 0) |
||||
debug("%s'%s' ", (i > 1) ? ", " : "", s); |
||||
debug("%s not supported\n", (i > 2) ? "are" : "is"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
uc_pdata->type = REGULATOR_TYPE_OTHER; |
||||
dev->priv = (void *)*p; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int pbias_regulator_get_value(struct udevice *dev) |
||||
{ |
||||
const struct pbias_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; |
||||
|
||||
debug("%s voltage id %s\n", p->name, |
||||
(reg & p->vmode) ? "3.0v" : "1.8v"); |
||||
return (reg & p->vmode) ? 3000000 : 1800000; |
||||
} |
||||
|
||||
static int pbias_regulator_set_value(struct udevice *dev, int uV) |
||||
{ |
||||
const struct pbias_reg_info *p = dev_get_priv(dev); |
||||
int rc; |
||||
u32 reg; |
||||
|
||||
debug("Setting %s voltage to %s\n", p->name, |
||||
(reg & p->vmode) ? "3.0v" : "1.8v"); |
||||
|
||||
rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); |
||||
if (rc) |
||||
return rc; |
||||
|
||||
if (uV == 3000000) |
||||
reg |= p->vmode; |
||||
else if (uV == 1800000) |
||||
reg &= ~p->vmode; |
||||
else |
||||
return -EINVAL; |
||||
|
||||
return pmic_write(dev->parent, 0, (uint8_t *)®, sizeof(reg)); |
||||
} |
||||
|
||||
static int pbias_regulator_get_enable(struct udevice *dev) |
||||
{ |
||||
const struct pbias_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; |
||||
|
||||
debug("%s id %s\n", p->name, |
||||
(reg & p->enable_mask) == (p->disable_val) ? "on" : "off"); |
||||
|
||||
return (reg & p->enable_mask) == (p->disable_val); |
||||
} |
||||
|
||||
static int pbias_regulator_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
const struct pbias_reg_info *p = dev_get_priv(dev); |
||||
int rc; |
||||
u32 reg; |
||||
|
||||
debug("Turning %s %s\n", enable ? "on" : "off", p->name); |
||||
|
||||
rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); |
||||
if (rc) |
||||
return rc; |
||||
|
||||
reg &= ~p->enable_mask; |
||||
if (enable) |
||||
reg |= p->enable; |
||||
else |
||||
reg |= p->disable_val; |
||||
|
||||
rc = pmic_write(dev->parent, 0, (uint8_t *)®, sizeof(reg)); |
||||
if (rc) |
||||
return rc; |
||||
|
||||
if (enable) |
||||
udelay(p->enable_time); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct dm_regulator_ops pbias_regulator_ops = { |
||||
.get_value = pbias_regulator_get_value, |
||||
.set_value = pbias_regulator_set_value, |
||||
.get_enable = pbias_regulator_get_enable, |
||||
.set_enable = pbias_regulator_set_enable, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(pbias_regulator) = { |
||||
.name = "pbias_regulator", |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &pbias_regulator_ops, |
||||
.probe = pbias_regulator_probe, |
||||
}; |
Loading…
Reference in new issue