summaryrefslogtreecommitdiff
path: root/drivers/regulator/ti-abb-regulator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/ti-abb-regulator.c')
-rw-r--r--drivers/regulator/ti-abb-regulator.c110
1 files changed, 46 insertions, 64 deletions
diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c
index d2f994298753..04133510e5af 100644
--- a/drivers/regulator/ti-abb-regulator.c
+++ b/drivers/regulator/ti-abb-regulator.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Texas Instruments SoC Adaptive Body Bias(ABB) Regulator
*
@@ -7,22 +8,12 @@
* Copyright (C) 2012-2013 Texas Instruments, Inc.
* Andrii Tseglytskyi <andrii.tseglytskyi@ti.com>
* Nishanth Menon <nm@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
@@ -42,7 +33,7 @@
/**
* struct ti_abb_info - ABB information per voltage setting
* @opp_sel: one of TI_ABB macro
- * @vset: (optional) vset value that LDOVBB needs to be overriden with.
+ * @vset: (optional) vset value that LDOVBB needs to be overridden with.
*
* Array of per voltage entries organized in the same order as regulator_desc's
* volt_table list. (selector is used to index from this array)
@@ -159,7 +150,7 @@ static inline void ti_abb_clear_txdone(const struct ti_abb *abb)
};
/**
- * ti_abb_wait_tranx() - waits for ABB tranxdone event
+ * ti_abb_wait_txdone() - waits for ABB tranxdone event
* @dev: device
* @abb: pointer to the abb instance
*
@@ -173,19 +164,14 @@ static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb)
while (timeout++ <= abb->settling_time) {
status = ti_abb_check_txdone(abb);
if (status)
- break;
+ return 0;
udelay(1);
}
- if (timeout > abb->settling_time) {
- dev_warn_ratelimited(dev,
- "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
- __func__, timeout, readl(abb->int_base));
- return -ETIMEDOUT;
- }
-
- return 0;
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
}
/**
@@ -205,19 +191,14 @@ static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb)
status = ti_abb_check_txdone(abb);
if (!status)
- break;
+ return 0;
udelay(1);
}
- if (timeout > abb->settling_time) {
- dev_warn_ratelimited(dev,
- "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
- __func__, timeout, readl(abb->int_base));
- return -ETIMEDOUT;
- }
-
- return 0;
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
}
/**
@@ -319,7 +300,7 @@ out:
*
* Return: 0 on success or appropriate error value when fails
*/
-static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
+static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
{
const struct regulator_desc *desc = rdev->desc;
struct ti_abb *abb = rdev_get_drvdata(rdev);
@@ -352,8 +333,17 @@ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
return ret;
}
- /* If data is exactly the same, then just update index, no change */
info = &abb->info[sel];
+ /*
+ * When Linux kernel is starting up, we aren't sure of the
+ * Bias configuration that bootloader has configured.
+ * So, we get to know the actual setting the first time
+ * we are asked to transition.
+ */
+ if (abb->current_info_idx == -EINVAL)
+ goto just_set_abb;
+
+ /* If data is exactly the same, then just update index, no change */
oinfo = &abb->info[abb->current_info_idx];
if (!memcmp(info, oinfo, sizeof(*info))) {
dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__,
@@ -361,6 +351,7 @@ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
goto out;
}
+just_set_abb:
ret = ti_abb_set_opp(rdev, abb, info);
out:
@@ -484,7 +475,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
/* Calculate cycle rate */
cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate);
- /* Calulate SR2_WTCNT_VALUE */
+ /* Calculate SR2_WTCNT_VALUE */
sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate);
dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
@@ -532,13 +523,13 @@ 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);
+ info = devm_kcalloc(dev, num_entries, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
abb->info = info;
- volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries,
+ volt_table = devm_kcalloc(dev, num_entries, sizeof(unsigned int),
GFP_KERNEL);
if (!volt_table)
return -ENOMEM;
@@ -629,7 +620,7 @@ check_abb:
return 0;
}
-static struct regulator_ops ti_abb_reg_ops = {
+static const struct regulator_ops ti_abb_reg_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = ti_abb_set_voltage_sel,
@@ -688,7 +679,7 @@ MODULE_DEVICE_TABLE(of, ti_abb_of_match);
* @pdev: ABB platform device
*
* Initializes an individual ABB LDO for required Body-Bias. ABB is used to
- * addional bias supply to SoC modules for power savings or mandatory stability
+ * additional bias supply to SoC modules for power savings or mandatory stability
* configuration at certain Operating Performance Points(OPPs).
*
* Return: 0 on success or appropriate error value when fails
@@ -696,7 +687,6 @@ MODULE_DEVICE_TABLE(of, ti_abb_of_match);
static int ti_abb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *match;
struct resource *res;
struct ti_abb *abb;
struct regulator_init_data *initdata = NULL;
@@ -707,27 +697,19 @@ static int ti_abb_probe(struct platform_device *pdev)
char *pname;
int ret = 0;
- match = of_match_device(ti_abb_of_match, dev);
- if (!match) {
- /* We do not expect this to happen */
- dev_err(dev, "%s: Unable to match device\n", __func__);
- return -ENODEV;
- }
- if (!match->data) {
- dev_err(dev, "%s: Bad data in match\n", __func__);
- return -EINVAL;
- }
-
abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL);
if (!abb)
return -ENOMEM;
- abb->regs = match->data;
+
+ abb->regs = device_get_match_data(dev);
+ if (!abb->regs) {
+ dev_err(dev, "%s: Bad data in match\n", __func__);
+ return -EINVAL;
+ }
/* Map ABB resources */
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);
+ abb->base = devm_platform_ioremap_resource_byname(pdev, "base-address");
if (IS_ERR(abb->base))
return PTR_ERR(abb->base);
@@ -735,15 +717,11 @@ static int ti_abb_probe(struct platform_device *pdev)
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);
+ abb->control_reg = devm_platform_ioremap_resource_byname(pdev, "control-address");
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);
+ abb->setup_reg = devm_platform_ioremap_resource_byname(pdev, "setup-address");
if (IS_ERR(abb->setup_reg))
return PTR_ERR(abb->setup_reg);
}
@@ -755,10 +733,13 @@ static int ti_abb_probe(struct platform_device *pdev)
return -ENODEV;
}
/*
- * We may have shared interrupt register offsets which are
- * write-1-to-clear between domains ensuring exclusivity.
+ * The MPU interrupt status register (PRM_IRQSTATUS_MPU) is
+ * shared between regulator-abb-{ivahd,dspeve,gpu} driver
+ * instances. Therefore use devm_ioremap() rather than
+ * devm_platform_ioremap_resource_byname() to avoid busy
+ * resource region conflicts.
*/
- abb->int_base = devm_ioremap_nocache(dev, res->start,
+ abb->int_base = devm_ioremap(dev, res->start,
resource_size(res));
if (!abb->int_base) {
dev_err(dev, "Unable to map '%s'\n", pname);
@@ -778,7 +759,7 @@ static int ti_abb_probe(struct platform_device *pdev)
* We may have shared efuse register offsets which are read-only
* between domains
*/
- abb->efuse_base = devm_ioremap_nocache(dev, res->start,
+ abb->efuse_base = devm_ioremap(dev, res->start,
resource_size(res));
if (!abb->efuse_base) {
dev_err(dev, "Unable to map '%s'\n", pname);
@@ -892,7 +873,8 @@ static struct platform_driver ti_abb_driver = {
.probe = ti_abb_probe,
.driver = {
.name = "ti_abb",
- .of_match_table = of_match_ptr(ti_abb_of_match),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = ti_abb_of_match,
},
};
module_platform_driver(ti_abb_driver);