18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 58c2ecf20Sopenharmony_ci * Mike Turquette <mturquette@ti.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Texas Instruments, Inc. 88c2ecf20Sopenharmony_ci * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com> 98c2ecf20Sopenharmony_ci * Nishanth Menon <nm@ti.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 138c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 168c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 178c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 188c2ecf20Sopenharmony_ci * GNU General Public License for more details. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/of_device.h> 268c2ecf20Sopenharmony_ci#include <linux/of.h> 278c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 288c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 298c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 308c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * ABB LDO operating states: 348c2ecf20Sopenharmony_ci * NOMINAL_OPP: bypasses the ABB LDO 358c2ecf20Sopenharmony_ci * FAST_OPP: sets ABB LDO to Forward Body-Bias 368c2ecf20Sopenharmony_ci * SLOW_OPP: sets ABB LDO to Reverse Body-Bias 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#define TI_ABB_NOMINAL_OPP 0 398c2ecf20Sopenharmony_ci#define TI_ABB_FAST_OPP 1 408c2ecf20Sopenharmony_ci#define TI_ABB_SLOW_OPP 3 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * struct ti_abb_info - ABB information per voltage setting 448c2ecf20Sopenharmony_ci * @opp_sel: one of TI_ABB macro 458c2ecf20Sopenharmony_ci * @vset: (optional) vset value that LDOVBB needs to be overriden with. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Array of per voltage entries organized in the same order as regulator_desc's 488c2ecf20Sopenharmony_ci * volt_table list. (selector is used to index from this array) 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistruct ti_abb_info { 518c2ecf20Sopenharmony_ci u32 opp_sel; 528c2ecf20Sopenharmony_ci u32 vset; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * struct ti_abb_reg - Register description for ABB block 578c2ecf20Sopenharmony_ci * @setup_off: setup register offset from base 588c2ecf20Sopenharmony_ci * @control_off: control register offset from base 598c2ecf20Sopenharmony_ci * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask 608c2ecf20Sopenharmony_ci * @fbb_sel_mask: setup register- FBB sel mask 618c2ecf20Sopenharmony_ci * @rbb_sel_mask: setup register- RBB sel mask 628c2ecf20Sopenharmony_ci * @sr2_en_mask: setup register- enable mask 638c2ecf20Sopenharmony_ci * @opp_change_mask: control register - mask to trigger LDOVBB change 648c2ecf20Sopenharmony_ci * @opp_sel_mask: control register - mask for mode to operate 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistruct ti_abb_reg { 678c2ecf20Sopenharmony_ci u32 setup_off; 688c2ecf20Sopenharmony_ci u32 control_off; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Setup register fields */ 718c2ecf20Sopenharmony_ci u32 sr2_wtcnt_value_mask; 728c2ecf20Sopenharmony_ci u32 fbb_sel_mask; 738c2ecf20Sopenharmony_ci u32 rbb_sel_mask; 748c2ecf20Sopenharmony_ci u32 sr2_en_mask; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Control register fields */ 778c2ecf20Sopenharmony_ci u32 opp_change_mask; 788c2ecf20Sopenharmony_ci u32 opp_sel_mask; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * struct ti_abb - ABB instance data 838c2ecf20Sopenharmony_ci * @rdesc: regulator descriptor 848c2ecf20Sopenharmony_ci * @clk: clock(usually sysclk) supplying ABB block 858c2ecf20Sopenharmony_ci * @base: base address of ABB block 868c2ecf20Sopenharmony_ci * @setup_reg: setup register of ABB block 878c2ecf20Sopenharmony_ci * @control_reg: control register of ABB block 888c2ecf20Sopenharmony_ci * @int_base: interrupt register base address 898c2ecf20Sopenharmony_ci * @efuse_base: (optional) efuse base address for ABB modes 908c2ecf20Sopenharmony_ci * @ldo_base: (optional) LDOVBB vset override base address 918c2ecf20Sopenharmony_ci * @regs: pointer to struct ti_abb_reg for ABB block 928c2ecf20Sopenharmony_ci * @txdone_mask: mask on int_base for tranxdone interrupt 938c2ecf20Sopenharmony_ci * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB 948c2ecf20Sopenharmony_ci * vset with value from efuse 958c2ecf20Sopenharmony_ci * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override 968c2ecf20Sopenharmony_ci * @info: array to per voltage ABB configuration 978c2ecf20Sopenharmony_ci * @current_info_idx: current index to info 988c2ecf20Sopenharmony_ci * @settling_time: SoC specific settling time for LDO VBB 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistruct ti_abb { 1018c2ecf20Sopenharmony_ci struct regulator_desc rdesc; 1028c2ecf20Sopenharmony_ci struct clk *clk; 1038c2ecf20Sopenharmony_ci void __iomem *base; 1048c2ecf20Sopenharmony_ci void __iomem *setup_reg; 1058c2ecf20Sopenharmony_ci void __iomem *control_reg; 1068c2ecf20Sopenharmony_ci void __iomem *int_base; 1078c2ecf20Sopenharmony_ci void __iomem *efuse_base; 1088c2ecf20Sopenharmony_ci void __iomem *ldo_base; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci const struct ti_abb_reg *regs; 1118c2ecf20Sopenharmony_ci u32 txdone_mask; 1128c2ecf20Sopenharmony_ci u32 ldovbb_override_mask; 1138c2ecf20Sopenharmony_ci u32 ldovbb_vset_mask; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci struct ti_abb_info *info; 1168c2ecf20Sopenharmony_ci int current_info_idx; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci u32 settling_time; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * ti_abb_rmw() - handy wrapper to set specific register bits 1238c2ecf20Sopenharmony_ci * @mask: mask for register field 1248c2ecf20Sopenharmony_ci * @value: value shifted to mask location and written 1258c2ecf20Sopenharmony_ci * @reg: register address 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * Return: final register value (may be unused) 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistatic inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u32 val; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci val = readl(reg); 1348c2ecf20Sopenharmony_ci val &= ~mask; 1358c2ecf20Sopenharmony_ci val |= (value << __ffs(mask)) & mask; 1368c2ecf20Sopenharmony_ci writel(val, reg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return val; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/** 1428c2ecf20Sopenharmony_ci * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status 1438c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * Return: true or false 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic inline bool ti_abb_check_txdone(const struct ti_abb *abb) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return !!(readl(abb->int_base) & abb->txdone_mask); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status 1548c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic inline void ti_abb_clear_txdone(const struct ti_abb *abb) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci writel(abb->txdone_mask, abb->int_base); 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * ti_abb_wait_tranx() - waits for ABB tranxdone event 1638c2ecf20Sopenharmony_ci * @dev: device 1648c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int timeout = 0; 1718c2ecf20Sopenharmony_ci bool status; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci while (timeout++ <= abb->settling_time) { 1748c2ecf20Sopenharmony_ci status = ti_abb_check_txdone(abb); 1758c2ecf20Sopenharmony_ci if (status) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci udelay(1); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n", 1828c2ecf20Sopenharmony_ci __func__, timeout, readl(abb->int_base)); 1838c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/** 1878c2ecf20Sopenharmony_ci * ti_abb_clear_all_txdone() - clears ABB tranxdone event 1888c2ecf20Sopenharmony_ci * @dev: device 1898c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_cistatic int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int timeout = 0; 1968c2ecf20Sopenharmony_ci bool status; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci while (timeout++ <= abb->settling_time) { 1998c2ecf20Sopenharmony_ci ti_abb_clear_txdone(abb); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci status = ti_abb_check_txdone(abb); 2028c2ecf20Sopenharmony_ci if (!status) 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci udelay(1); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n", 2098c2ecf20Sopenharmony_ci __func__, timeout, readl(abb->int_base)); 2108c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/** 2148c2ecf20Sopenharmony_ci * ti_abb_program_ldovbb() - program LDOVBB register for override value 2158c2ecf20Sopenharmony_ci * @dev: device 2168c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 2178c2ecf20Sopenharmony_ci * @info: ABB info to program 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb, 2208c2ecf20Sopenharmony_ci struct ti_abb_info *info) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u32 val; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci val = readl(abb->ldo_base); 2258c2ecf20Sopenharmony_ci /* clear up previous values */ 2268c2ecf20Sopenharmony_ci val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci switch (info->opp_sel) { 2298c2ecf20Sopenharmony_ci case TI_ABB_SLOW_OPP: 2308c2ecf20Sopenharmony_ci case TI_ABB_FAST_OPP: 2318c2ecf20Sopenharmony_ci val |= abb->ldovbb_override_mask; 2328c2ecf20Sopenharmony_ci val |= info->vset << __ffs(abb->ldovbb_vset_mask); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci writel(val, abb->ldo_base); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/** 2408c2ecf20Sopenharmony_ci * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias 2418c2ecf20Sopenharmony_ci * @rdev: regulator device 2428c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 2438c2ecf20Sopenharmony_ci * @info: ABB info to program 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Return: 0 on success or appropriate error value when fails 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, 2488c2ecf20Sopenharmony_ci struct ti_abb_info *info) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci const struct ti_abb_reg *regs = abb->regs; 2518c2ecf20Sopenharmony_ci struct device *dev = &rdev->dev; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = ti_abb_clear_all_txdone(dev, abb); 2558c2ecf20Sopenharmony_ci if (ret) 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci switch (info->opp_sel) { 2618c2ecf20Sopenharmony_ci case TI_ABB_SLOW_OPP: 2628c2ecf20Sopenharmony_ci ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case TI_ABB_FAST_OPP: 2658c2ecf20Sopenharmony_ci ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* program next state of ABB ldo */ 2708c2ecf20Sopenharmony_ci ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * program LDO VBB vset override if needed for !bypass mode 2748c2ecf20Sopenharmony_ci * XXX: Do not switch sequence - for !bypass, LDO override reset *must* 2758c2ecf20Sopenharmony_ci * be performed *before* switch to bias mode else VBB glitches. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci if (abb->ldo_base && info->opp_sel != TI_ABB_NOMINAL_OPP) 2788c2ecf20Sopenharmony_ci ti_abb_program_ldovbb(dev, abb, info); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Initiate ABB ldo change */ 2818c2ecf20Sopenharmony_ci ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Wait for ABB LDO to complete transition to new Bias setting */ 2848c2ecf20Sopenharmony_ci ret = ti_abb_wait_txdone(dev, abb); 2858c2ecf20Sopenharmony_ci if (ret) 2868c2ecf20Sopenharmony_ci goto out; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = ti_abb_clear_all_txdone(dev, abb); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto out; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * Reset LDO VBB vset override bypass mode 2948c2ecf20Sopenharmony_ci * XXX: Do not switch sequence - for bypass, LDO override reset *must* 2958c2ecf20Sopenharmony_ci * be performed *after* switch to bypass else VBB glitches. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci if (abb->ldo_base && info->opp_sel == TI_ABB_NOMINAL_OPP) 2988c2ecf20Sopenharmony_ci ti_abb_program_ldovbb(dev, abb, info); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciout: 3018c2ecf20Sopenharmony_ci return ret; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO 3068c2ecf20Sopenharmony_ci * @rdev: regulator device 3078c2ecf20Sopenharmony_ci * @sel: selector to index into required ABB LDO settings (maps to 3088c2ecf20Sopenharmony_ci * regulator descriptor's volt_table) 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Return: 0 on success or appropriate error value when fails 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci const struct regulator_desc *desc = rdev->desc; 3158c2ecf20Sopenharmony_ci struct ti_abb *abb = rdev_get_drvdata(rdev); 3168c2ecf20Sopenharmony_ci struct device *dev = &rdev->dev; 3178c2ecf20Sopenharmony_ci struct ti_abb_info *info, *oinfo; 3188c2ecf20Sopenharmony_ci int ret = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!abb) { 3218c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, "%s: No regulator drvdata\n", 3228c2ecf20Sopenharmony_ci __func__); 3238c2ecf20Sopenharmony_ci return -ENODEV; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!desc->n_voltages || !abb->info) { 3278c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, 3288c2ecf20Sopenharmony_ci "%s: No valid voltage table entries?\n", 3298c2ecf20Sopenharmony_ci __func__); 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (sel >= desc->n_voltages) { 3348c2ecf20Sopenharmony_ci dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__, 3358c2ecf20Sopenharmony_ci sel, desc->n_voltages); 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* If we are in the same index as we were, nothing to do here! */ 3408c2ecf20Sopenharmony_ci if (sel == abb->current_info_idx) { 3418c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel); 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci info = &abb->info[sel]; 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * When Linux kernel is starting up, we are'nt sure of the 3488c2ecf20Sopenharmony_ci * Bias configuration that bootloader has configured. 3498c2ecf20Sopenharmony_ci * So, we get to know the actual setting the first time 3508c2ecf20Sopenharmony_ci * we are asked to transition. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (abb->current_info_idx == -EINVAL) 3538c2ecf20Sopenharmony_ci goto just_set_abb; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* If data is exactly the same, then just update index, no change */ 3568c2ecf20Sopenharmony_ci oinfo = &abb->info[abb->current_info_idx]; 3578c2ecf20Sopenharmony_ci if (!memcmp(info, oinfo, sizeof(*info))) { 3588c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__, 3598c2ecf20Sopenharmony_ci sel, abb->current_info_idx); 3608c2ecf20Sopenharmony_ci goto out; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cijust_set_abb: 3648c2ecf20Sopenharmony_ci ret = ti_abb_set_opp(rdev, abb, info); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciout: 3678c2ecf20Sopenharmony_ci if (!ret) 3688c2ecf20Sopenharmony_ci abb->current_info_idx = sel; 3698c2ecf20Sopenharmony_ci else 3708c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, 3718c2ecf20Sopenharmony_ci "%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n", 3728c2ecf20Sopenharmony_ci __func__, desc->volt_table[sel], sel, 3738c2ecf20Sopenharmony_ci info->opp_sel, ret); 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting 3798c2ecf20Sopenharmony_ci * @rdev: regulator device 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * Return: 0 on success or appropriate error value when fails 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_cistatic int ti_abb_get_voltage_sel(struct regulator_dev *rdev) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci const struct regulator_desc *desc = rdev->desc; 3868c2ecf20Sopenharmony_ci struct ti_abb *abb = rdev_get_drvdata(rdev); 3878c2ecf20Sopenharmony_ci struct device *dev = &rdev->dev; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!abb) { 3908c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, "%s: No regulator drvdata\n", 3918c2ecf20Sopenharmony_ci __func__); 3928c2ecf20Sopenharmony_ci return -ENODEV; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!desc->n_voltages || !abb->info) { 3968c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, 3978c2ecf20Sopenharmony_ci "%s: No valid voltage table entries?\n", 3988c2ecf20Sopenharmony_ci __func__); 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (abb->current_info_idx >= (int)desc->n_voltages) { 4038c2ecf20Sopenharmony_ci dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n", 4048c2ecf20Sopenharmony_ci __func__, abb->current_info_idx, desc->n_voltages); 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return abb->current_info_idx; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/** 4128c2ecf20Sopenharmony_ci * ti_abb_init_timings() - setup ABB clock timing for the current platform 4138c2ecf20Sopenharmony_ci * @dev: device 4148c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Return: 0 if timing is updated, else returns error result. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci u32 clock_cycles; 4218c2ecf20Sopenharmony_ci u32 clk_rate, sr2_wt_cnt_val, cycle_rate; 4228c2ecf20Sopenharmony_ci const struct ti_abb_reg *regs = abb->regs; 4238c2ecf20Sopenharmony_ci int ret; 4248c2ecf20Sopenharmony_ci char *pname = "ti,settling-time"; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* read device tree properties */ 4278c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time); 4288c2ecf20Sopenharmony_ci if (ret) { 4298c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* ABB LDO cannot be settle in 0 time */ 4348c2ecf20Sopenharmony_ci if (!abb->settling_time) { 4358c2ecf20Sopenharmony_ci dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pname = "ti,clock-cycles"; 4408c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, pname, &clock_cycles); 4418c2ecf20Sopenharmony_ci if (ret) { 4428c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci /* ABB LDO cannot be settle in 0 clock cycles */ 4468c2ecf20Sopenharmony_ci if (!clock_cycles) { 4478c2ecf20Sopenharmony_ci dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); 4488c2ecf20Sopenharmony_ci return -EINVAL; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci abb->clk = devm_clk_get(dev, NULL); 4528c2ecf20Sopenharmony_ci if (IS_ERR(abb->clk)) { 4538c2ecf20Sopenharmony_ci ret = PTR_ERR(abb->clk); 4548c2ecf20Sopenharmony_ci dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret); 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* 4598c2ecf20Sopenharmony_ci * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a 4608c2ecf20Sopenharmony_ci * transition and must be programmed with the correct time at boot. 4618c2ecf20Sopenharmony_ci * The value programmed into the register is the number of SYS_CLK 4628c2ecf20Sopenharmony_ci * clock cycles that match a given wall time profiled for the ldo. 4638c2ecf20Sopenharmony_ci * This value depends on: 4648c2ecf20Sopenharmony_ci * settling time of ldo in micro-seconds (varies per OMAP family) 4658c2ecf20Sopenharmony_ci * # of clock cycles per SYS_CLK period (varies per OMAP family) 4668c2ecf20Sopenharmony_ci * the SYS_CLK frequency in MHz (varies per board) 4678c2ecf20Sopenharmony_ci * The formula is: 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * ldo settling time (in micro-seconds) 4708c2ecf20Sopenharmony_ci * SR2_WTCNT_VALUE = ------------------------------------------ 4718c2ecf20Sopenharmony_ci * (# system clock cycles) * (sys_clk period) 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Put another way: 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate)) 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * To avoid dividing by zero multiply both "# clock cycles" and 4788c2ecf20Sopenharmony_ci * "settling time" by 10 such that the final result is the one we want. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Convert SYS_CLK rate to MHz & prevent divide by zero */ 4828c2ecf20Sopenharmony_ci clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Calculate cycle rate */ 4858c2ecf20Sopenharmony_ci cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Calulate SR2_WTCNT_VALUE */ 4888c2ecf20Sopenharmony_ci sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, 4918c2ecf20Sopenharmony_ci clk_get_rate(abb->clk), sr2_wt_cnt_val); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * ti_abb_init_table() - Initialize ABB table from device tree 5008c2ecf20Sopenharmony_ci * @dev: device 5018c2ecf20Sopenharmony_ci * @abb: pointer to the abb instance 5028c2ecf20Sopenharmony_ci * @rinit_data: regulator initdata 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * Return: 0 on success or appropriate error value when fails 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic int ti_abb_init_table(struct device *dev, struct ti_abb *abb, 5078c2ecf20Sopenharmony_ci struct regulator_init_data *rinit_data) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct ti_abb_info *info; 5108c2ecf20Sopenharmony_ci const u32 num_values = 6; 5118c2ecf20Sopenharmony_ci char *pname = "ti,abb_info"; 5128c2ecf20Sopenharmony_ci u32 i; 5138c2ecf20Sopenharmony_ci unsigned int *volt_table; 5148c2ecf20Sopenharmony_ci int num_entries, min_uV = INT_MAX, max_uV = 0; 5158c2ecf20Sopenharmony_ci struct regulation_constraints *c = &rinit_data->constraints; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * Each abb_info is a set of n-tuple, where n is num_values, consisting 5198c2ecf20Sopenharmony_ci * of voltage and a set of detection logic for ABB information for that 5208c2ecf20Sopenharmony_ci * voltage to apply. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci num_entries = of_property_count_u32_elems(dev->of_node, pname); 5238c2ecf20Sopenharmony_ci if (num_entries < 0) { 5248c2ecf20Sopenharmony_ci dev_err(dev, "No '%s' property?\n", pname); 5258c2ecf20Sopenharmony_ci return num_entries; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (!num_entries || (num_entries % num_values)) { 5298c2ecf20Sopenharmony_ci dev_err(dev, "All '%s' list entries need %d vals\n", pname, 5308c2ecf20Sopenharmony_ci num_values); 5318c2ecf20Sopenharmony_ci return -EINVAL; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci num_entries /= num_values; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci info = devm_kcalloc(dev, num_entries, sizeof(*info), GFP_KERNEL); 5368c2ecf20Sopenharmony_ci if (!info) 5378c2ecf20Sopenharmony_ci return -ENOMEM; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci abb->info = info; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci volt_table = devm_kcalloc(dev, num_entries, sizeof(unsigned int), 5428c2ecf20Sopenharmony_ci GFP_KERNEL); 5438c2ecf20Sopenharmony_ci if (!volt_table) 5448c2ecf20Sopenharmony_ci return -ENOMEM; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci abb->rdesc.n_voltages = num_entries; 5478c2ecf20Sopenharmony_ci abb->rdesc.volt_table = volt_table; 5488c2ecf20Sopenharmony_ci /* We do not know where the OPP voltage is at the moment */ 5498c2ecf20Sopenharmony_ci abb->current_info_idx = -EINVAL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci for (i = 0; i < num_entries; i++, info++, volt_table++) { 5528c2ecf20Sopenharmony_ci u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; 5538c2ecf20Sopenharmony_ci u32 efuse_val; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* NOTE: num_values should equal to entries picked up here */ 5568c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, i * num_values, 5578c2ecf20Sopenharmony_ci volt_table); 5588c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, 5598c2ecf20Sopenharmony_ci i * num_values + 1, &info->opp_sel); 5608c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, 5618c2ecf20Sopenharmony_ci i * num_values + 2, &efuse_offset); 5628c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, 5638c2ecf20Sopenharmony_ci i * num_values + 3, &rbb_mask); 5648c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, 5658c2ecf20Sopenharmony_ci i * num_values + 4, &fbb_mask); 5668c2ecf20Sopenharmony_ci of_property_read_u32_index(dev->of_node, pname, 5678c2ecf20Sopenharmony_ci i * num_values + 5, &vset_mask); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci dev_dbg(dev, 5708c2ecf20Sopenharmony_ci "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", 5718c2ecf20Sopenharmony_ci i, *volt_table, info->opp_sel, efuse_offset, rbb_mask, 5728c2ecf20Sopenharmony_ci fbb_mask, vset_mask); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Find min/max for voltage set */ 5758c2ecf20Sopenharmony_ci if (min_uV > *volt_table) 5768c2ecf20Sopenharmony_ci min_uV = *volt_table; 5778c2ecf20Sopenharmony_ci if (max_uV < *volt_table) 5788c2ecf20Sopenharmony_ci max_uV = *volt_table; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (!abb->efuse_base) { 5818c2ecf20Sopenharmony_ci /* Ignore invalid data, but warn to help cleanup */ 5828c2ecf20Sopenharmony_ci if (efuse_offset || rbb_mask || fbb_mask || vset_mask) 5838c2ecf20Sopenharmony_ci dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n", 5848c2ecf20Sopenharmony_ci pname, *volt_table); 5858c2ecf20Sopenharmony_ci goto check_abb; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci efuse_val = readl(abb->efuse_base + efuse_offset); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Use ABB recommendation from Efuse */ 5918c2ecf20Sopenharmony_ci if (efuse_val & rbb_mask) 5928c2ecf20Sopenharmony_ci info->opp_sel = TI_ABB_SLOW_OPP; 5938c2ecf20Sopenharmony_ci else if (efuse_val & fbb_mask) 5948c2ecf20Sopenharmony_ci info->opp_sel = TI_ABB_FAST_OPP; 5958c2ecf20Sopenharmony_ci else if (rbb_mask || fbb_mask) 5968c2ecf20Sopenharmony_ci info->opp_sel = TI_ABB_NOMINAL_OPP; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci dev_dbg(dev, 5998c2ecf20Sopenharmony_ci "[%d]v=%d efusev=0x%x final ABB=%d\n", 6008c2ecf20Sopenharmony_ci i, *volt_table, efuse_val, info->opp_sel); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Use recommended Vset bits from Efuse */ 6038c2ecf20Sopenharmony_ci if (!abb->ldo_base) { 6048c2ecf20Sopenharmony_ci if (vset_mask) 6058c2ecf20Sopenharmony_ci dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n", 6068c2ecf20Sopenharmony_ci pname, *volt_table, vset_mask); 6078c2ecf20Sopenharmony_ci continue; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci info->vset = (efuse_val & vset_mask) >> __ffs(vset_mask); 6108c2ecf20Sopenharmony_ci dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset); 6118c2ecf20Sopenharmony_cicheck_abb: 6128c2ecf20Sopenharmony_ci switch (info->opp_sel) { 6138c2ecf20Sopenharmony_ci case TI_ABB_NOMINAL_OPP: 6148c2ecf20Sopenharmony_ci case TI_ABB_FAST_OPP: 6158c2ecf20Sopenharmony_ci case TI_ABB_SLOW_OPP: 6168c2ecf20Sopenharmony_ci /* Valid values */ 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci default: 6198c2ecf20Sopenharmony_ci dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n", 6208c2ecf20Sopenharmony_ci __func__, i, *volt_table, info->opp_sel); 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Setup the min/max voltage constraints from the supported list */ 6268c2ecf20Sopenharmony_ci c->min_uV = min_uV; 6278c2ecf20Sopenharmony_ci c->max_uV = max_uV; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic const struct regulator_ops ti_abb_reg_ops = { 6338c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_table, 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci .set_voltage_sel = ti_abb_set_voltage_sel, 6368c2ecf20Sopenharmony_ci .get_voltage_sel = ti_abb_get_voltage_sel, 6378c2ecf20Sopenharmony_ci}; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/* Default ABB block offsets, IF this changes in future, create new one */ 6408c2ecf20Sopenharmony_cistatic const struct ti_abb_reg abb_regs_v1 = { 6418c2ecf20Sopenharmony_ci /* WARNING: registers are wrongly documented in TRM */ 6428c2ecf20Sopenharmony_ci .setup_off = 0x04, 6438c2ecf20Sopenharmony_ci .control_off = 0x00, 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci .sr2_wtcnt_value_mask = (0xff << 8), 6468c2ecf20Sopenharmony_ci .fbb_sel_mask = (0x01 << 2), 6478c2ecf20Sopenharmony_ci .rbb_sel_mask = (0x01 << 1), 6488c2ecf20Sopenharmony_ci .sr2_en_mask = (0x01 << 0), 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci .opp_change_mask = (0x01 << 2), 6518c2ecf20Sopenharmony_ci .opp_sel_mask = (0x03 << 0), 6528c2ecf20Sopenharmony_ci}; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic const struct ti_abb_reg abb_regs_v2 = { 6558c2ecf20Sopenharmony_ci .setup_off = 0x00, 6568c2ecf20Sopenharmony_ci .control_off = 0x04, 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci .sr2_wtcnt_value_mask = (0xff << 8), 6598c2ecf20Sopenharmony_ci .fbb_sel_mask = (0x01 << 2), 6608c2ecf20Sopenharmony_ci .rbb_sel_mask = (0x01 << 1), 6618c2ecf20Sopenharmony_ci .sr2_en_mask = (0x01 << 0), 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci .opp_change_mask = (0x01 << 2), 6648c2ecf20Sopenharmony_ci .opp_sel_mask = (0x03 << 0), 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic const struct ti_abb_reg abb_regs_generic = { 6688c2ecf20Sopenharmony_ci .sr2_wtcnt_value_mask = (0xff << 8), 6698c2ecf20Sopenharmony_ci .fbb_sel_mask = (0x01 << 2), 6708c2ecf20Sopenharmony_ci .rbb_sel_mask = (0x01 << 1), 6718c2ecf20Sopenharmony_ci .sr2_en_mask = (0x01 << 0), 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci .opp_change_mask = (0x01 << 2), 6748c2ecf20Sopenharmony_ci .opp_sel_mask = (0x03 << 0), 6758c2ecf20Sopenharmony_ci}; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic const struct of_device_id ti_abb_of_match[] = { 6788c2ecf20Sopenharmony_ci {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, 6798c2ecf20Sopenharmony_ci {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, 6808c2ecf20Sopenharmony_ci {.compatible = "ti,abb-v3", .data = &abb_regs_generic}, 6818c2ecf20Sopenharmony_ci { }, 6828c2ecf20Sopenharmony_ci}; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_abb_of_match); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/** 6878c2ecf20Sopenharmony_ci * ti_abb_probe() - Initialize an ABB ldo instance 6888c2ecf20Sopenharmony_ci * @pdev: ABB platform device 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Initializes an individual ABB LDO for required Body-Bias. ABB is used to 6918c2ecf20Sopenharmony_ci * addional bias supply to SoC modules for power savings or mandatory stability 6928c2ecf20Sopenharmony_ci * configuration at certain Operating Performance Points(OPPs). 6938c2ecf20Sopenharmony_ci * 6948c2ecf20Sopenharmony_ci * Return: 0 on success or appropriate error value when fails 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_cistatic int ti_abb_probe(struct platform_device *pdev) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6998c2ecf20Sopenharmony_ci const struct of_device_id *match; 7008c2ecf20Sopenharmony_ci struct resource *res; 7018c2ecf20Sopenharmony_ci struct ti_abb *abb; 7028c2ecf20Sopenharmony_ci struct regulator_init_data *initdata = NULL; 7038c2ecf20Sopenharmony_ci struct regulator_dev *rdev = NULL; 7048c2ecf20Sopenharmony_ci struct regulator_desc *desc; 7058c2ecf20Sopenharmony_ci struct regulation_constraints *c; 7068c2ecf20Sopenharmony_ci struct regulator_config config = { }; 7078c2ecf20Sopenharmony_ci char *pname; 7088c2ecf20Sopenharmony_ci int ret = 0; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci match = of_match_device(ti_abb_of_match, dev); 7118c2ecf20Sopenharmony_ci if (!match) { 7128c2ecf20Sopenharmony_ci /* We do not expect this to happen */ 7138c2ecf20Sopenharmony_ci dev_err(dev, "%s: Unable to match device\n", __func__); 7148c2ecf20Sopenharmony_ci return -ENODEV; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci if (!match->data) { 7178c2ecf20Sopenharmony_ci dev_err(dev, "%s: Bad data in match\n", __func__); 7188c2ecf20Sopenharmony_ci return -EINVAL; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL); 7228c2ecf20Sopenharmony_ci if (!abb) 7238c2ecf20Sopenharmony_ci return -ENOMEM; 7248c2ecf20Sopenharmony_ci abb->regs = match->data; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* Map ABB resources */ 7278c2ecf20Sopenharmony_ci if (abb->regs->setup_off || abb->regs->control_off) { 7288c2ecf20Sopenharmony_ci pname = "base-address"; 7298c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7308c2ecf20Sopenharmony_ci abb->base = devm_ioremap_resource(dev, res); 7318c2ecf20Sopenharmony_ci if (IS_ERR(abb->base)) 7328c2ecf20Sopenharmony_ci return PTR_ERR(abb->base); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci abb->setup_reg = abb->base + abb->regs->setup_off; 7358c2ecf20Sopenharmony_ci abb->control_reg = abb->base + abb->regs->control_off; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci pname = "control-address"; 7398c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7408c2ecf20Sopenharmony_ci abb->control_reg = devm_ioremap_resource(dev, res); 7418c2ecf20Sopenharmony_ci if (IS_ERR(abb->control_reg)) 7428c2ecf20Sopenharmony_ci return PTR_ERR(abb->control_reg); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pname = "setup-address"; 7458c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7468c2ecf20Sopenharmony_ci abb->setup_reg = devm_ioremap_resource(dev, res); 7478c2ecf20Sopenharmony_ci if (IS_ERR(abb->setup_reg)) 7488c2ecf20Sopenharmony_ci return PTR_ERR(abb->setup_reg); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci pname = "int-address"; 7528c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7538c2ecf20Sopenharmony_ci if (!res) { 7548c2ecf20Sopenharmony_ci dev_err(dev, "Missing '%s' IO resource\n", pname); 7558c2ecf20Sopenharmony_ci return -ENODEV; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci /* 7588c2ecf20Sopenharmony_ci * We may have shared interrupt register offsets which are 7598c2ecf20Sopenharmony_ci * write-1-to-clear between domains ensuring exclusivity. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci abb->int_base = devm_ioremap(dev, res->start, 7628c2ecf20Sopenharmony_ci resource_size(res)); 7638c2ecf20Sopenharmony_ci if (!abb->int_base) { 7648c2ecf20Sopenharmony_ci dev_err(dev, "Unable to map '%s'\n", pname); 7658c2ecf20Sopenharmony_ci return -ENOMEM; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* Map Optional resources */ 7698c2ecf20Sopenharmony_ci pname = "efuse-address"; 7708c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7718c2ecf20Sopenharmony_ci if (!res) { 7728c2ecf20Sopenharmony_ci dev_dbg(dev, "Missing '%s' IO resource\n", pname); 7738c2ecf20Sopenharmony_ci ret = -ENODEV; 7748c2ecf20Sopenharmony_ci goto skip_opt; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* 7788c2ecf20Sopenharmony_ci * We may have shared efuse register offsets which are read-only 7798c2ecf20Sopenharmony_ci * between domains 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci abb->efuse_base = devm_ioremap(dev, res->start, 7828c2ecf20Sopenharmony_ci resource_size(res)); 7838c2ecf20Sopenharmony_ci if (!abb->efuse_base) { 7848c2ecf20Sopenharmony_ci dev_err(dev, "Unable to map '%s'\n", pname); 7858c2ecf20Sopenharmony_ci return -ENOMEM; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci pname = "ldo-address"; 7898c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); 7908c2ecf20Sopenharmony_ci if (!res) { 7918c2ecf20Sopenharmony_ci dev_dbg(dev, "Missing '%s' IO resource\n", pname); 7928c2ecf20Sopenharmony_ci ret = -ENODEV; 7938c2ecf20Sopenharmony_ci goto skip_opt; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci abb->ldo_base = devm_ioremap_resource(dev, res); 7968c2ecf20Sopenharmony_ci if (IS_ERR(abb->ldo_base)) 7978c2ecf20Sopenharmony_ci return PTR_ERR(abb->ldo_base); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* IF ldo_base is set, the following are mandatory */ 8008c2ecf20Sopenharmony_ci pname = "ti,ldovbb-override-mask"; 8018c2ecf20Sopenharmony_ci ret = 8028c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, pname, 8038c2ecf20Sopenharmony_ci &abb->ldovbb_override_mask); 8048c2ecf20Sopenharmony_ci if (ret) { 8058c2ecf20Sopenharmony_ci dev_err(dev, "Missing '%s' (%d)\n", pname, ret); 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci if (!abb->ldovbb_override_mask) { 8098c2ecf20Sopenharmony_ci dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); 8108c2ecf20Sopenharmony_ci return -EINVAL; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci pname = "ti,ldovbb-vset-mask"; 8148c2ecf20Sopenharmony_ci ret = 8158c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, pname, 8168c2ecf20Sopenharmony_ci &abb->ldovbb_vset_mask); 8178c2ecf20Sopenharmony_ci if (ret) { 8188c2ecf20Sopenharmony_ci dev_err(dev, "Missing '%s' (%d)\n", pname, ret); 8198c2ecf20Sopenharmony_ci return ret; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci if (!abb->ldovbb_vset_mask) { 8228c2ecf20Sopenharmony_ci dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); 8238c2ecf20Sopenharmony_ci return -EINVAL; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciskip_opt: 8278c2ecf20Sopenharmony_ci pname = "ti,tranxdone-status-mask"; 8288c2ecf20Sopenharmony_ci ret = 8298c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, pname, 8308c2ecf20Sopenharmony_ci &abb->txdone_mask); 8318c2ecf20Sopenharmony_ci if (ret) { 8328c2ecf20Sopenharmony_ci dev_err(dev, "Missing '%s' (%d)\n", pname, ret); 8338c2ecf20Sopenharmony_ci return ret; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci if (!abb->txdone_mask) { 8368c2ecf20Sopenharmony_ci dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); 8378c2ecf20Sopenharmony_ci return -EINVAL; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci initdata = of_get_regulator_init_data(dev, pdev->dev.of_node, 8418c2ecf20Sopenharmony_ci &abb->rdesc); 8428c2ecf20Sopenharmony_ci if (!initdata) { 8438c2ecf20Sopenharmony_ci dev_err(dev, "%s: Unable to alloc regulator init data\n", 8448c2ecf20Sopenharmony_ci __func__); 8458c2ecf20Sopenharmony_ci return -ENOMEM; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* init ABB opp_sel table */ 8498c2ecf20Sopenharmony_ci ret = ti_abb_init_table(dev, abb, initdata); 8508c2ecf20Sopenharmony_ci if (ret) 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* init ABB timing */ 8548c2ecf20Sopenharmony_ci ret = ti_abb_init_timings(dev, abb); 8558c2ecf20Sopenharmony_ci if (ret) 8568c2ecf20Sopenharmony_ci return ret; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci desc = &abb->rdesc; 8598c2ecf20Sopenharmony_ci desc->name = dev_name(dev); 8608c2ecf20Sopenharmony_ci desc->owner = THIS_MODULE; 8618c2ecf20Sopenharmony_ci desc->type = REGULATOR_VOLTAGE; 8628c2ecf20Sopenharmony_ci desc->ops = &ti_abb_reg_ops; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci c = &initdata->constraints; 8658c2ecf20Sopenharmony_ci if (desc->n_voltages > 1) 8668c2ecf20Sopenharmony_ci c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; 8678c2ecf20Sopenharmony_ci c->always_on = true; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci config.dev = dev; 8708c2ecf20Sopenharmony_ci config.init_data = initdata; 8718c2ecf20Sopenharmony_ci config.driver_data = abb; 8728c2ecf20Sopenharmony_ci config.of_node = pdev->dev.of_node; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci rdev = devm_regulator_register(dev, desc, &config); 8758c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 8768c2ecf20Sopenharmony_ci ret = PTR_ERR(rdev); 8778c2ecf20Sopenharmony_ci dev_err(dev, "%s: failed to register regulator(%d)\n", 8788c2ecf20Sopenharmony_ci __func__, ret); 8798c2ecf20Sopenharmony_ci return ret; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rdev); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Enable the ldo if not already done by bootloader */ 8848c2ecf20Sopenharmony_ci ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ti_abb"); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic struct platform_driver ti_abb_driver = { 8928c2ecf20Sopenharmony_ci .probe = ti_abb_probe, 8938c2ecf20Sopenharmony_ci .driver = { 8948c2ecf20Sopenharmony_ci .name = "ti_abb", 8958c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ti_abb_of_match), 8968c2ecf20Sopenharmony_ci }, 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_cimodule_platform_driver(ti_abb_driver); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver"); 9018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc."); 9028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 903