|
@@ -54,8 +54,8 @@ struct ti_abb_info {
|
|
|
|
|
|
/**
|
|
|
* struct ti_abb_reg - Register description for ABB block
|
|
|
- * @setup_reg: setup register offset from base
|
|
|
- * @control_reg: control register offset from base
|
|
|
+ * @setup_off: setup register offset from base
|
|
|
+ * @control_off: control register offset from base
|
|
|
* @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask
|
|
|
* @fbb_sel_mask: setup register- FBB sel mask
|
|
|
* @rbb_sel_mask: setup register- RBB sel mask
|
|
@@ -64,8 +64,8 @@ struct ti_abb_info {
|
|
|
* @opp_sel_mask: control register - mask for mode to operate
|
|
|
*/
|
|
|
struct ti_abb_reg {
|
|
|
- u32 setup_reg;
|
|
|
- u32 control_reg;
|
|
|
+ u32 setup_off;
|
|
|
+ u32 control_off;
|
|
|
|
|
|
/* Setup register fields */
|
|
|
u32 sr2_wtcnt_value_mask;
|
|
@@ -83,6 +83,8 @@ struct ti_abb_reg {
|
|
|
* @rdesc: regulator descriptor
|
|
|
* @clk: clock(usually sysclk) supplying ABB block
|
|
|
* @base: base address of ABB block
|
|
|
+ * @setup_reg: setup register of ABB block
|
|
|
+ * @control_reg: control register of ABB block
|
|
|
* @int_base: interrupt register base address
|
|
|
* @efuse_base: (optional) efuse base address for ABB modes
|
|
|
* @ldo_base: (optional) LDOVBB vset override base address
|
|
@@ -99,6 +101,8 @@ struct ti_abb {
|
|
|
struct regulator_desc rdesc;
|
|
|
struct clk *clk;
|
|
|
void __iomem *base;
|
|
|
+ void __iomem *setup_reg;
|
|
|
+ void __iomem *control_reg;
|
|
|
void __iomem *int_base;
|
|
|
void __iomem *efuse_base;
|
|
|
void __iomem *ldo_base;
|
|
@@ -118,20 +122,18 @@ struct ti_abb {
|
|
|
* ti_abb_rmw() - handy wrapper to set specific register bits
|
|
|
* @mask: mask for register field
|
|
|
* @value: value shifted to mask location and written
|
|
|
- * @offset: offset of register
|
|
|
- * @base: base address
|
|
|
+ * @reg: register address
|
|
|
*
|
|
|
* Return: final register value (may be unused)
|
|
|
*/
|
|
|
-static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset,
|
|
|
- void __iomem *base)
|
|
|
+static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg)
|
|
|
{
|
|
|
u32 val;
|
|
|
|
|
|
- val = readl(base + offset);
|
|
|
+ val = readl(reg);
|
|
|
val &= ~mask;
|
|
|
val |= (value << __ffs(mask)) & mask;
|
|
|
- writel(val, base + offset);
|
|
|
+ writel(val, reg);
|
|
|
|
|
|
return val;
|
|
|
}
|
|
@@ -263,21 +265,19 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
|
|
|
- ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg,
|
|
|
- abb->base);
|
|
|
+ ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg);
|
|
|
|
|
|
switch (info->opp_sel) {
|
|
|
case TI_ABB_SLOW_OPP:
|
|
|
- ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base);
|
|
|
+ ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg);
|
|
|
break;
|
|
|
case TI_ABB_FAST_OPP:
|
|
|
- ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base);
|
|
|
+ ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
/* program next state of ABB ldo */
|
|
|
- ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg,
|
|
|
- abb->base);
|
|
|
+ ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg);
|
|
|
|
|
|
/*
|
|
|
* program LDO VBB vset override if needed for !bypass mode
|
|
@@ -288,7 +288,7 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
|
|
|
ti_abb_program_ldovbb(dev, abb, info);
|
|
|
|
|
|
/* Initiate ABB ldo change */
|
|
|
- ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base);
|
|
|
+ ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg);
|
|
|
|
|
|
/* Wait for ABB LDO to complete transition to new Bias setting */
|
|
|
ret = ti_abb_wait_txdone(dev, abb);
|
|
@@ -490,8 +490,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
|
|
|
dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
|
|
|
clk_get_rate(abb->clk), sr2_wt_cnt_val);
|
|
|
|
|
|
- ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg,
|
|
|
- abb->base);
|
|
|
+ ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -508,32 +507,24 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb,
|
|
|
struct regulator_init_data *rinit_data)
|
|
|
{
|
|
|
struct ti_abb_info *info;
|
|
|
- const struct property *prop;
|
|
|
- const __be32 *abb_info;
|
|
|
const u32 num_values = 6;
|
|
|
char *pname = "ti,abb_info";
|
|
|
- u32 num_entries, i;
|
|
|
+ u32 i;
|
|
|
unsigned int *volt_table;
|
|
|
- int min_uV = INT_MAX, max_uV = 0;
|
|
|
+ int num_entries, min_uV = INT_MAX, max_uV = 0;
|
|
|
struct regulation_constraints *c = &rinit_data->constraints;
|
|
|
|
|
|
- prop = of_find_property(dev->of_node, pname, NULL);
|
|
|
- if (!prop) {
|
|
|
- dev_err(dev, "No '%s' property?\n", pname);
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- if (!prop->value) {
|
|
|
- dev_err(dev, "Empty '%s' property?\n", pname);
|
|
|
- return -ENODATA;
|
|
|
- }
|
|
|
-
|
|
|
/*
|
|
|
* Each abb_info is a set of n-tuple, where n is num_values, consisting
|
|
|
* of voltage and a set of detection logic for ABB information for that
|
|
|
* voltage to apply.
|
|
|
*/
|
|
|
- num_entries = prop->length / sizeof(u32);
|
|
|
+ num_entries = of_property_count_u32_elems(dev->of_node, pname);
|
|
|
+ if (num_entries < 0) {
|
|
|
+ dev_err(dev, "No '%s' property?\n", pname);
|
|
|
+ return num_entries;
|
|
|
+ }
|
|
|
+
|
|
|
if (!num_entries || (num_entries % num_values)) {
|
|
|
dev_err(dev, "All '%s' list entries need %d vals\n", pname,
|
|
|
num_values);
|
|
@@ -542,38 +533,38 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb,
|
|
|
num_entries /= num_values;
|
|
|
|
|
|
info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL);
|
|
|
- if (!info) {
|
|
|
- dev_err(dev, "Can't allocate info table for '%s' property\n",
|
|
|
- pname);
|
|
|
+ if (!info)
|
|
|
return -ENOMEM;
|
|
|
- }
|
|
|
+
|
|
|
abb->info = info;
|
|
|
|
|
|
volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries,
|
|
|
GFP_KERNEL);
|
|
|
- if (!volt_table) {
|
|
|
- dev_err(dev, "Can't allocate voltage table for '%s' property\n",
|
|
|
- pname);
|
|
|
+ if (!volt_table)
|
|
|
return -ENOMEM;
|
|
|
- }
|
|
|
|
|
|
abb->rdesc.n_voltages = num_entries;
|
|
|
abb->rdesc.volt_table = volt_table;
|
|
|
/* We do not know where the OPP voltage is at the moment */
|
|
|
abb->current_info_idx = -EINVAL;
|
|
|
|
|
|
- abb_info = prop->value;
|
|
|
for (i = 0; i < num_entries; i++, info++, volt_table++) {
|
|
|
u32 efuse_offset, rbb_mask, fbb_mask, vset_mask;
|
|
|
u32 efuse_val;
|
|
|
|
|
|
/* NOTE: num_values should equal to entries picked up here */
|
|
|
- *volt_table = be32_to_cpup(abb_info++);
|
|
|
- info->opp_sel = be32_to_cpup(abb_info++);
|
|
|
- efuse_offset = be32_to_cpup(abb_info++);
|
|
|
- rbb_mask = be32_to_cpup(abb_info++);
|
|
|
- fbb_mask = be32_to_cpup(abb_info++);
|
|
|
- vset_mask = be32_to_cpup(abb_info++);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname, i * num_values,
|
|
|
+ volt_table);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname,
|
|
|
+ i * num_values + 1, &info->opp_sel);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname,
|
|
|
+ i * num_values + 2, &efuse_offset);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname,
|
|
|
+ i * num_values + 3, &rbb_mask);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname,
|
|
|
+ i * num_values + 4, &fbb_mask);
|
|
|
+ of_property_read_u32_index(dev->of_node, pname,
|
|
|
+ i * num_values + 5, &vset_mask);
|
|
|
|
|
|
dev_dbg(dev,
|
|
|
"[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n",
|
|
@@ -648,8 +639,8 @@ static struct regulator_ops ti_abb_reg_ops = {
|
|
|
/* Default ABB block offsets, IF this changes in future, create new one */
|
|
|
static const struct ti_abb_reg abb_regs_v1 = {
|
|
|
/* WARNING: registers are wrongly documented in TRM */
|
|
|
- .setup_reg = 0x04,
|
|
|
- .control_reg = 0x00,
|
|
|
+ .setup_off = 0x04,
|
|
|
+ .control_off = 0x00,
|
|
|
|
|
|
.sr2_wtcnt_value_mask = (0xff << 8),
|
|
|
.fbb_sel_mask = (0x01 << 2),
|
|
@@ -661,8 +652,8 @@ static const struct ti_abb_reg abb_regs_v1 = {
|
|
|
};
|
|
|
|
|
|
static const struct ti_abb_reg abb_regs_v2 = {
|
|
|
- .setup_reg = 0x00,
|
|
|
- .control_reg = 0x04,
|
|
|
+ .setup_off = 0x00,
|
|
|
+ .control_off = 0x04,
|
|
|
|
|
|
.sr2_wtcnt_value_mask = (0xff << 8),
|
|
|
.fbb_sel_mask = (0x01 << 2),
|
|
@@ -673,9 +664,20 @@ static const struct ti_abb_reg abb_regs_v2 = {
|
|
|
.opp_sel_mask = (0x03 << 0),
|
|
|
};
|
|
|
|
|
|
+static const struct ti_abb_reg abb_regs_generic = {
|
|
|
+ .sr2_wtcnt_value_mask = (0xff << 8),
|
|
|
+ .fbb_sel_mask = (0x01 << 2),
|
|
|
+ .rbb_sel_mask = (0x01 << 1),
|
|
|
+ .sr2_en_mask = (0x01 << 0),
|
|
|
+
|
|
|
+ .opp_change_mask = (0x01 << 2),
|
|
|
+ .opp_sel_mask = (0x03 << 0),
|
|
|
+};
|
|
|
+
|
|
|
static const struct of_device_id ti_abb_of_match[] = {
|
|
|
{.compatible = "ti,abb-v1", .data = &abb_regs_v1},
|
|
|
{.compatible = "ti,abb-v2", .data = &abb_regs_v2},
|
|
|
+ {.compatible = "ti,abb-v3", .data = &abb_regs_generic},
|
|
|
{ },
|
|
|
};
|
|
|
|
|
@@ -722,11 +724,29 @@ static int ti_abb_probe(struct platform_device *pdev)
|
|
|
abb->regs = match->data;
|
|
|
|
|
|
/* Map ABB resources */
|
|
|
- pname = "base-address";
|
|
|
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
|
|
|
- abb->base = devm_ioremap_resource(dev, res);
|
|
|
- if (IS_ERR(abb->base))
|
|
|
- return PTR_ERR(abb->base);
|
|
|
+ if (abb->regs->setup_off || abb->regs->control_off) {
|
|
|
+ pname = "base-address";
|
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
|
|
|
+ abb->base = devm_ioremap_resource(dev, res);
|
|
|
+ if (IS_ERR(abb->base))
|
|
|
+ return PTR_ERR(abb->base);
|
|
|
+
|
|
|
+ abb->setup_reg = abb->base + abb->regs->setup_off;
|
|
|
+ abb->control_reg = abb->base + abb->regs->control_off;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ pname = "control-address";
|
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
|
|
|
+ abb->control_reg = devm_ioremap_resource(dev, res);
|
|
|
+ if (IS_ERR(abb->control_reg))
|
|
|
+ return PTR_ERR(abb->control_reg);
|
|
|
+
|
|
|
+ pname = "setup-address";
|
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
|
|
|
+ abb->setup_reg = devm_ioremap_resource(dev, res);
|
|
|
+ if (IS_ERR(abb->setup_reg))
|
|
|
+ return PTR_ERR(abb->setup_reg);
|
|
|
+ }
|
|
|
|
|
|
pname = "int-address";
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
|
|
@@ -860,7 +880,7 @@ skip_opt:
|
|
|
platform_set_drvdata(pdev, rdev);
|
|
|
|
|
|
/* Enable the ldo if not already done by bootloader */
|
|
|
- ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base);
|
|
|
+ ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg);
|
|
|
|
|
|
return 0;
|
|
|
}
|