diff options
Diffstat (limited to 'drivers/regulator/ti-abb-regulator.c')
-rw-r--r-- | drivers/regulator/ti-abb-regulator.c | 140 |
1 files changed, 80 insertions, 60 deletions
diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index b187b6bba7ad..a2dabb575b97 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -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; } |