// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator * * Copyright (C) 2011 Texas Instruments, Inc. * Mike Turquette <[email protected]> * * Copyright (C) 2012-2013 Texas Instruments, Inc. * Andrii Tseglytskyi <[email protected]> * Nishanth Menon <[email protected]> */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> /* * ABB LDO operating states: * NOMINAL_OPP: bypasses the ABB LDO * FAST_OPP: sets ABB LDO to Forward Body-Bias * SLOW_OPP: sets ABB LDO to Reverse Body-Bias */ #define TI_ABB_NOMINAL_OPP … #define TI_ABB_FAST_OPP … #define TI_ABB_SLOW_OPP … /** * 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 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) */ struct ti_abb_info { … }; /** * struct ti_abb_reg - Register description for ABB block * @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 * @sr2_en_mask: setup register- enable mask * @opp_change_mask: control register - mask to trigger LDOVBB change * @opp_sel_mask: control register - mask for mode to operate */ struct ti_abb_reg { … }; /** * struct ti_abb - ABB instance data * @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 * @regs: pointer to struct ti_abb_reg for ABB block * @txdone_mask: mask on int_base for tranxdone interrupt * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB * vset with value from efuse * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override * @info: array to per voltage ABB configuration * @current_info_idx: current index to info * @settling_time: SoC specific settling time for LDO VBB */ 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 * @reg: register address * * Return: final register value (may be unused) */ static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg) { … } /** * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status * @abb: pointer to the abb instance * * Return: true or false */ static inline bool ti_abb_check_txdone(const struct ti_abb *abb) { … } /** * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status * @abb: pointer to the abb instance */ static inline void ti_abb_clear_txdone(const struct ti_abb *abb) { writel(abb->txdone_mask, abb->int_base); }; /** * ti_abb_wait_txdone() - waits for ABB tranxdone event * @dev: device * @abb: pointer to the abb instance * * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. */ static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb) { … } /** * ti_abb_clear_all_txdone() - clears ABB tranxdone event * @dev: device * @abb: pointer to the abb instance * * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. */ static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb) { … } /** * ti_abb_program_ldovbb() - program LDOVBB register for override value * @dev: device * @abb: pointer to the abb instance * @info: ABB info to program */ static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb, struct ti_abb_info *info) { … } /** * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias * @rdev: regulator device * @abb: pointer to the abb instance * @info: ABB info to program * * Return: 0 on success or appropriate error value when fails */ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, struct ti_abb_info *info) { … } /** * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO * @rdev: regulator device * @sel: selector to index into required ABB LDO settings (maps to * regulator descriptor's volt_table) * * Return: 0 on success or appropriate error value when fails */ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) { … } /** * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting * @rdev: regulator device * * Return: 0 on success or appropriate error value when fails */ static int ti_abb_get_voltage_sel(struct regulator_dev *rdev) { … } /** * ti_abb_init_timings() - setup ABB clock timing for the current platform * @dev: device * @abb: pointer to the abb instance * * Return: 0 if timing is updated, else returns error result. */ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) { … } /** * ti_abb_init_table() - Initialize ABB table from device tree * @dev: device * @abb: pointer to the abb instance * @rinit_data: regulator initdata * * Return: 0 on success or appropriate error value when fails */ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, struct regulator_init_data *rinit_data) { … } static const 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 = …; static const struct ti_abb_reg abb_regs_v2 = …; static const struct ti_abb_reg abb_regs_generic = …; static const struct of_device_id ti_abb_of_match[] = …; MODULE_DEVICE_TABLE(of, ti_abb_of_match); /** * ti_abb_probe() - Initialize an ABB ldo instance * @pdev: ABB platform device * * Initializes an individual ABB LDO for required Body-Bias. ABB is used to * 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 */ static int ti_abb_probe(struct platform_device *pdev) { … } MODULE_ALIAS(…) …; static struct platform_driver ti_abb_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;