cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm8400-regulator.c (7818B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// Regulator support for WM8400
      4//
      5// Copyright 2008 Wolfson Microelectronics PLC.
      6//
      7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
      8
      9#include <linux/bug.h>
     10#include <linux/err.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/regulator/driver.h>
     14#include <linux/mfd/wm8400-private.h>
     15
     16static const struct linear_range wm8400_ldo_ranges[] = {
     17	REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
     18	REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
     19};
     20
     21static const struct regulator_ops wm8400_ldo_ops = {
     22	.is_enabled = regulator_is_enabled_regmap,
     23	.enable = regulator_enable_regmap,
     24	.disable = regulator_disable_regmap,
     25	.list_voltage = regulator_list_voltage_linear_range,
     26	.get_voltage_sel = regulator_get_voltage_sel_regmap,
     27	.set_voltage_sel = regulator_set_voltage_sel_regmap,
     28	.map_voltage = regulator_map_voltage_linear_range,
     29};
     30
     31static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
     32{
     33	struct regmap *rmap = rdev_get_regmap(dev);
     34	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
     35	u16 data[2];
     36	int ret;
     37
     38	ret = regmap_bulk_read(rmap, WM8400_DCDC1_CONTROL_1 + offset, data, 2);
     39	if (ret != 0)
     40		return 0;
     41
     42	/* Datasheet: hibernate */
     43	if (data[0] & WM8400_DC1_SLEEP)
     44		return REGULATOR_MODE_STANDBY;
     45
     46	/* Datasheet: standby */
     47	if (!(data[0] & WM8400_DC1_ACTIVE))
     48		return REGULATOR_MODE_IDLE;
     49
     50	/* Datasheet: active with or without force PWM */
     51	if (data[1] & WM8400_DC1_FRC_PWM)
     52		return REGULATOR_MODE_FAST;
     53	else
     54		return REGULATOR_MODE_NORMAL;
     55}
     56
     57static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
     58{
     59	struct regmap *rmap = rdev_get_regmap(dev);
     60	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
     61	int ret;
     62
     63	switch (mode) {
     64	case REGULATOR_MODE_FAST:
     65		/* Datasheet: active with force PWM */
     66		ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,
     67				      WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
     68		if (ret != 0)
     69			return ret;
     70
     71		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
     72				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
     73				       WM8400_DC1_ACTIVE);
     74
     75	case REGULATOR_MODE_NORMAL:
     76		/* Datasheet: active */
     77		ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,
     78				      WM8400_DC1_FRC_PWM, 0);
     79		if (ret != 0)
     80			return ret;
     81
     82		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
     83				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
     84				       WM8400_DC1_ACTIVE);
     85
     86	case REGULATOR_MODE_IDLE:
     87		/* Datasheet: standby */
     88		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
     89				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);
     90	default:
     91		return -EINVAL;
     92	}
     93}
     94
     95static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
     96						 int input_uV, int output_uV,
     97						 int load_uA)
     98{
     99	return REGULATOR_MODE_NORMAL;
    100}
    101
    102static const struct regulator_ops wm8400_dcdc_ops = {
    103	.is_enabled = regulator_is_enabled_regmap,
    104	.enable = regulator_enable_regmap,
    105	.disable = regulator_disable_regmap,
    106	.list_voltage = regulator_list_voltage_linear,
    107	.map_voltage = regulator_map_voltage_linear,
    108	.get_voltage_sel = regulator_get_voltage_sel_regmap,
    109	.set_voltage_sel = regulator_set_voltage_sel_regmap,
    110	.get_mode = wm8400_dcdc_get_mode,
    111	.set_mode = wm8400_dcdc_set_mode,
    112	.get_optimum_mode = wm8400_dcdc_get_optimum_mode,
    113};
    114
    115static struct regulator_desc regulators[] = {
    116	{
    117		.name = "LDO1",
    118		.id = WM8400_LDO1,
    119		.ops = &wm8400_ldo_ops,
    120		.enable_reg = WM8400_LDO1_CONTROL,
    121		.enable_mask = WM8400_LDO1_ENA,
    122		.n_voltages = WM8400_LDO1_VSEL_MASK + 1,
    123		.linear_ranges = wm8400_ldo_ranges,
    124		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
    125		.vsel_reg = WM8400_LDO1_CONTROL,
    126		.vsel_mask = WM8400_LDO1_VSEL_MASK,
    127		.type = REGULATOR_VOLTAGE,
    128		.owner = THIS_MODULE,
    129	},
    130	{
    131		.name = "LDO2",
    132		.id = WM8400_LDO2,
    133		.ops = &wm8400_ldo_ops,
    134		.enable_reg = WM8400_LDO2_CONTROL,
    135		.enable_mask = WM8400_LDO2_ENA,
    136		.n_voltages = WM8400_LDO2_VSEL_MASK + 1,
    137		.linear_ranges = wm8400_ldo_ranges,
    138		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
    139		.type = REGULATOR_VOLTAGE,
    140		.vsel_reg = WM8400_LDO2_CONTROL,
    141		.vsel_mask = WM8400_LDO2_VSEL_MASK,
    142		.owner = THIS_MODULE,
    143	},
    144	{
    145		.name = "LDO3",
    146		.id = WM8400_LDO3,
    147		.ops = &wm8400_ldo_ops,
    148		.enable_reg = WM8400_LDO3_CONTROL,
    149		.enable_mask = WM8400_LDO3_ENA,
    150		.n_voltages = WM8400_LDO3_VSEL_MASK + 1,
    151		.linear_ranges = wm8400_ldo_ranges,
    152		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
    153		.vsel_reg = WM8400_LDO3_CONTROL,
    154		.vsel_mask = WM8400_LDO3_VSEL_MASK,
    155		.type = REGULATOR_VOLTAGE,
    156		.owner = THIS_MODULE,
    157	},
    158	{
    159		.name = "LDO4",
    160		.id = WM8400_LDO4,
    161		.ops = &wm8400_ldo_ops,
    162		.enable_reg = WM8400_LDO4_CONTROL,
    163		.enable_mask = WM8400_LDO4_ENA,
    164		.n_voltages = WM8400_LDO4_VSEL_MASK + 1,
    165		.linear_ranges = wm8400_ldo_ranges,
    166		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
    167		.vsel_reg = WM8400_LDO4_CONTROL,
    168		.vsel_mask = WM8400_LDO4_VSEL_MASK,
    169		.type = REGULATOR_VOLTAGE,
    170		.owner = THIS_MODULE,
    171	},
    172	{
    173		.name = "DCDC1",
    174		.id = WM8400_DCDC1,
    175		.ops = &wm8400_dcdc_ops,
    176		.enable_reg = WM8400_DCDC1_CONTROL_1,
    177		.enable_mask = WM8400_DC1_ENA_MASK,
    178		.n_voltages = WM8400_DC1_VSEL_MASK + 1,
    179		.vsel_reg = WM8400_DCDC1_CONTROL_1,
    180		.vsel_mask = WM8400_DC1_VSEL_MASK,
    181		.min_uV = 850000,
    182		.uV_step = 25000,
    183		.type = REGULATOR_VOLTAGE,
    184		.owner = THIS_MODULE,
    185	},
    186	{
    187		.name = "DCDC2",
    188		.id = WM8400_DCDC2,
    189		.ops = &wm8400_dcdc_ops,
    190		.enable_reg = WM8400_DCDC2_CONTROL_1,
    191		.enable_mask = WM8400_DC2_ENA_MASK,
    192		.n_voltages = WM8400_DC2_VSEL_MASK + 1,
    193		.vsel_reg = WM8400_DCDC2_CONTROL_1,
    194		.vsel_mask = WM8400_DC2_VSEL_MASK,
    195		.min_uV = 850000,
    196		.uV_step = 25000,
    197		.type = REGULATOR_VOLTAGE,
    198		.owner = THIS_MODULE,
    199	},
    200};
    201
    202static int wm8400_regulator_probe(struct platform_device *pdev)
    203{
    204	struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]);
    205	struct regulator_config config = { };
    206	struct regulator_dev *rdev;
    207
    208	config.dev = &pdev->dev;
    209	config.init_data = dev_get_platdata(&pdev->dev);
    210	config.driver_data = wm8400;
    211	config.regmap = wm8400->regmap;
    212
    213	rdev = devm_regulator_register(&pdev->dev, &regulators[pdev->id],
    214				       &config);
    215	if (IS_ERR(rdev))
    216		return PTR_ERR(rdev);
    217
    218	platform_set_drvdata(pdev, rdev);
    219
    220	return 0;
    221}
    222
    223static struct platform_driver wm8400_regulator_driver = {
    224	.driver = {
    225		.name = "wm8400-regulator",
    226	},
    227	.probe = wm8400_regulator_probe,
    228};
    229
    230/**
    231 * wm8400_register_regulator - enable software control of a WM8400 regulator
    232 *
    233 * This function enables software control of a WM8400 regulator via
    234 * the regulator API.  It is intended to be called from the
    235 * platform_init() callback of the WM8400 MFD driver.
    236 *
    237 * @dev:      The WM8400 device to operate on.
    238 * @reg:      The regulator to control.
    239 * @initdata: Regulator initdata for the regulator.
    240 */
    241int wm8400_register_regulator(struct device *dev, int reg,
    242			      struct regulator_init_data *initdata)
    243{
    244	struct wm8400 *wm8400 = dev_get_drvdata(dev);
    245
    246	if (wm8400->regulators[reg].name)
    247		return -EBUSY;
    248
    249	initdata->driver_data = wm8400;
    250
    251	wm8400->regulators[reg].name = "wm8400-regulator";
    252	wm8400->regulators[reg].id = reg;
    253	wm8400->regulators[reg].dev.parent = dev;
    254	wm8400->regulators[reg].dev.platform_data = initdata;
    255
    256	return platform_device_register(&wm8400->regulators[reg]);
    257}
    258EXPORT_SYMBOL_GPL(wm8400_register_regulator);
    259
    260static int __init wm8400_regulator_init(void)
    261{
    262	return platform_driver_register(&wm8400_regulator_driver);
    263}
    264subsys_initcall(wm8400_regulator_init);
    265
    266static void __exit wm8400_regulator_exit(void)
    267{
    268	platform_driver_unregister(&wm8400_regulator_driver);
    269}
    270module_exit(wm8400_regulator_exit);
    271
    272MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
    273MODULE_DESCRIPTION("WM8400 regulator driver");
    274MODULE_LICENSE("GPL");
    275MODULE_ALIAS("platform:wm8400-regulator");