// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018-2021 Unisoc Inc.
 */

#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>

/*
 * SC2730 regulator base address
 */
#define SC2730_REGULATOR_BASE		0x1800

/*
 * SC2730 regulator lock register
 */
#define SC2730_WR_UNLOCK_VALUE		0x6e7f
#define SC2730_PWR_WR_PROT		(SC2730_REGULATOR_BASE + 0x3d0)

/*
 * SC2730 enable register
 */
#define SC2730_POWER_PD_SW		(SC2730_REGULATOR_BASE + 0x01c)
#define SC2730_LDO_VDDRF18_PD		(SC2730_REGULATOR_BASE + 0x10c)
#define SC2730_LDO_VDDCAMIO_PD		(SC2730_REGULATOR_BASE + 0x118)
#define SC2730_LDO_VDDWCN_PD		(SC2730_REGULATOR_BASE + 0x11c)
#define SC2730_LDO_VDDCAMD1_PD		(SC2730_REGULATOR_BASE + 0x128)
#define SC2730_LDO_VDDCAMD0_PD		(SC2730_REGULATOR_BASE + 0x134)
#define SC2730_LDO_VDDRF1V25_PD		(SC2730_REGULATOR_BASE + 0x140)
#define SC2730_LDO_AVDD12_PD		(SC2730_REGULATOR_BASE + 0x14c)
#define SC2730_LDO_VDDCAMA0_PD		(SC2730_REGULATOR_BASE + 0x158)
#define SC2730_LDO_VDDCAMA1_PD		(SC2730_REGULATOR_BASE + 0x164)
#define SC2730_LDO_VDDCAMMOT_PD		(SC2730_REGULATOR_BASE + 0x170)
#define SC2730_LDO_VDDSIM2_PD		(SC2730_REGULATOR_BASE + 0x194)
#define SC2730_LDO_VDDEMMCCORE_PD	(SC2730_REGULATOR_BASE + 0x1a0)
#define SC2730_LDO_VDDSDCORE_PD		(SC2730_REGULATOR_BASE + 0x1ac)
#define SC2730_LDO_VDDSDIO_PD		(SC2730_REGULATOR_BASE + 0x1b8)
#define SC2730_LDO_VDDWIFIPA_PD		(SC2730_REGULATOR_BASE + 0x1d0)
#define SC2730_LDO_VDDUSB33_PD		(SC2730_REGULATOR_BASE + 0x1e8)
#define SC2730_LDO_VDDLDO0_PD		(SC2730_REGULATOR_BASE + 0x1f4)
#define SC2730_LDO_VDDLDO1_PD		(SC2730_REGULATOR_BASE + 0x200)
#define SC2730_LDO_VDDLDO2_PD		(SC2730_REGULATOR_BASE + 0x20c)
#define SC2730_LDO_VDDKPLED_PD		(SC2730_REGULATOR_BASE + 0x38c)

/*
 * SC2730 enable mask
 */
#define SC2730_DCDC_CPU_PD_MASK		BIT(4)
#define SC2730_DCDC_GPU_PD_MASK		BIT(3)
#define SC2730_DCDC_CORE_PD_MASK	BIT(5)
#define SC2730_DCDC_MODEM_PD_MASK	BIT(11)
#define SC2730_DCDC_MEM_PD_MASK		BIT(6)
#define SC2730_DCDC_MEMQ_PD_MASK	BIT(12)
#define SC2730_DCDC_GEN0_PD_MASK	BIT(8)
#define SC2730_DCDC_GEN1_PD_MASK	BIT(7)
#define SC2730_DCDC_SRAM_PD_MASK	BIT(13)
#define SC2730_LDO_AVDD18_PD_MASK	BIT(2)
#define SC2730_LDO_VDDRF18_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMIO_PD_MASK	BIT(0)
#define SC2730_LDO_VDDWCN_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMD1_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMD0_PD_MASK	BIT(0)
#define SC2730_LDO_VDDRF1V25_PD_MASK	BIT(0)
#define SC2730_LDO_AVDD12_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMA0_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMA1_PD_MASK	BIT(0)
#define SC2730_LDO_VDDCAMMOT_PD_MASK	BIT(0)
#define SC2730_LDO_VDDSIM2_PD_MASK	BIT(0)
#define SC2730_LDO_VDDEMMCCORE_PD_MASK	BIT(0)
#define SC2730_LDO_VDDSDCORE_PD_MASK	BIT(0)
#define SC2730_LDO_VDDSDIO_PD_MASK	BIT(0)
#define SC2730_LDO_VDD28_PD_MASK	BIT(1)
#define SC2730_LDO_VDDWIFIPA_PD_MASK	BIT(0)
#define SC2730_LDO_VDD18_DCXO_PD_MASK	BIT(10)
#define SC2730_LDO_VDDUSB33_PD_MASK	BIT(0)
#define SC2730_LDO_VDDLDO0_PD_MASK	BIT(0)
#define SC2730_LDO_VDDLDO1_PD_MASK	BIT(0)
#define SC2730_LDO_VDDLDO2_PD_MASK	BIT(0)
#define SC2730_LDO_VDDKPLED_PD_MASK	BIT(15)

/*
 * SC2730 vsel register
 */
#define SC2730_DCDC_CPU_VOL		(SC2730_REGULATOR_BASE + 0x44)
#define SC2730_DCDC_GPU_VOL		(SC2730_REGULATOR_BASE + 0x54)
#define SC2730_DCDC_CORE_VOL		(SC2730_REGULATOR_BASE + 0x64)
#define SC2730_DCDC_MODEM_VOL		(SC2730_REGULATOR_BASE + 0x74)
#define SC2730_DCDC_MEM_VOL		(SC2730_REGULATOR_BASE + 0x84)
#define SC2730_DCDC_MEMQ_VOL		(SC2730_REGULATOR_BASE + 0x94)
#define SC2730_DCDC_GEN0_VOL		(SC2730_REGULATOR_BASE + 0xa4)
#define SC2730_DCDC_GEN1_VOL		(SC2730_REGULATOR_BASE + 0xb4)
#define SC2730_DCDC_SRAM_VOL		(SC2730_REGULATOR_BASE + 0xdc)
#define SC2730_LDO_AVDD18_VOL		(SC2730_REGULATOR_BASE + 0x104)
#define SC2730_LDO_VDDRF18_VOL		(SC2730_REGULATOR_BASE + 0x110)
#define SC2730_LDO_VDDCAMIO_VOL		(SC2730_REGULATOR_BASE + 0x28)
#define SC2730_LDO_VDDWCN_VOL		(SC2730_REGULATOR_BASE + 0x120)
#define SC2730_LDO_VDDCAMD1_VOL		(SC2730_REGULATOR_BASE + 0x12c)
#define SC2730_LDO_VDDCAMD0_VOL		(SC2730_REGULATOR_BASE + 0x138)
#define SC2730_LDO_VDDRF1V25_VOL	(SC2730_REGULATOR_BASE + 0x144)
#define SC2730_LDO_AVDD12_VOL		(SC2730_REGULATOR_BASE + 0x150)
#define SC2730_LDO_VDDCAMA0_VOL		(SC2730_REGULATOR_BASE + 0x15c)
#define SC2730_LDO_VDDCAMA1_VOL		(SC2730_REGULATOR_BASE + 0x168)
#define SC2730_LDO_VDDCAMMOT_VOL	(SC2730_REGULATOR_BASE + 0x174)
#define SC2730_LDO_VDDSIM2_VOL		(SC2730_REGULATOR_BASE + 0x198)
#define SC2730_LDO_VDDEMMCCORE_VOL	(SC2730_REGULATOR_BASE + 0x1a4)
#define SC2730_LDO_VDDSDCORE_VOL	(SC2730_REGULATOR_BASE + 0x1b0)
#define SC2730_LDO_VDDSDIO_VOL		(SC2730_REGULATOR_BASE + 0x1bc)
#define SC2730_LDO_VDD28_VOL		(SC2730_REGULATOR_BASE + 0x1c8)
#define SC2730_LDO_VDDWIFIPA_VOL	(SC2730_REGULATOR_BASE + 0x1d4)
#define SC2730_LDO_VDD18_DCXO_VOL	(SC2730_REGULATOR_BASE + 0x1e0)
#define SC2730_LDO_VDDUSB33_VOL		(SC2730_REGULATOR_BASE + 0x1ec)
#define SC2730_LDO_VDDLDO0_VOL		(SC2730_REGULATOR_BASE + 0x1f8)
#define SC2730_LDO_VDDLDO1_VOL		(SC2730_REGULATOR_BASE + 0x204)
#define SC2730_LDO_VDDLDO2_VOL		(SC2730_REGULATOR_BASE + 0x210)
#define SC2730_LDO_VDDKPLED_VOL		(SC2730_REGULATOR_BASE + 0x38c)

/*
 * SC2730 vsel register mask
 */
#define SC2730_DCDC_CPU_VOL_MASK	GENMASK(8, 0)
#define SC2730_DCDC_GPU_VOL_MASK	GENMASK(8, 0)
#define SC2730_DCDC_CORE_VOL_MASK	GENMASK(8, 0)
#define SC2730_DCDC_MODEM_VOL_MASK	GENMASK(8, 0)
#define SC2730_DCDC_MEM_VOL_MASK	GENMASK(7, 0)
#define SC2730_DCDC_MEMQ_VOL_MASK	GENMASK(8, 0)
#define SC2730_DCDC_GEN0_VOL_MASK	GENMASK(7, 0)
#define SC2730_DCDC_GEN1_VOL_MASK	GENMASK(7, 0)
#define SC2730_DCDC_SRAM_VOL_MASK	GENMASK(8, 0)
#define SC2730_LDO_AVDD18_VOL_MASK	GENMASK(5, 0)
#define SC2730_LDO_VDDRF18_VOL_MASK	GENMASK(5, 0)
#define SC2730_LDO_VDDCAMIO_VOL_MASK	GENMASK(5, 0)
#define SC2730_LDO_VDDWCN_VOL_MASK	GENMASK(5, 0)
#define SC2730_LDO_VDDCAMD1_VOL_MASK	GENMASK(4, 0)
#define SC2730_LDO_VDDCAMD0_VOL_MASK	GENMASK(4, 0)
#define SC2730_LDO_VDDRF1V25_VOL_MASK	GENMASK(4, 0)
#define SC2730_LDO_AVDD12_VOL_MASK	GENMASK(4, 0)
#define SC2730_LDO_VDDCAMA0_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDCAMA1_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDCAMMOT_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDSIM2_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDEMMCCORE_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDSDCORE_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDSDIO_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDD28_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDWIFIPA_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDD18_DCXO_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDUSB33_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDLDO0_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDLDO1_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDLDO2_VOL_MASK	GENMASK(7, 0)
#define SC2730_LDO_VDDKPLED_VOL_MASK	GENMASK(14, 7)

enum sc2730_regulator_id {
	SC2730_DCDC_CPU,
	SC2730_DCDC_GPU,
	SC2730_DCDC_CORE,
	SC2730_DCDC_MODEM,
	SC2730_DCDC_MEM,
	SC2730_DCDC_MEMQ,
	SC2730_DCDC_GEN0,
	SC2730_DCDC_GEN1,
	SC2730_DCDC_SRAM,
	SC2730_LDO_AVDD18,
	SC2730_LDO_VDDRF18,
	SC2730_LDO_VDDCAMIO,
	SC2730_LDO_VDDWCN,
	SC2730_LDO_VDDCAMD1,
	SC2730_LDO_VDDCAMD0,
	SC2730_LDO_VDDRF1V25,
	SC2730_LDO_AVDD12,
	SC2730_LDO_VDDCAMA0,
	SC2730_LDO_VDDCAMA1,
	SC2730_LDO_VDDCAMMOT,
	SC2730_LDO_VDDSIM2,
	SC2730_LDO_VDDEMMCCORE,
	SC2730_LDO_VDDSDCORE,
	SC2730_LDO_VDDSDIO,
	SC2730_LDO_VDD28,
	SC2730_LDO_VDDWIFIPA,
	SC2730_LDO_VDD18_DCXO,
	SC2730_LDO_VDDUSB33,
	SC2730_LDO_VDDLDO0,
	SC2730_LDO_VDDLDO1,
	SC2730_LDO_VDDLDO2,
	SC2730_LDO_VDDKPLED,
};

static const struct regulator_ops sc2730_regu_linear_ops = {
	.enable = regulator_enable_regmap,
	.disable = regulator_disable_regmap,
	.is_enabled = regulator_is_enabled_regmap,
	.list_voltage = regulator_list_voltage_linear,
	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,
};

#define SC2730_REGU_LINEAR(_id, of_name, en_reg, en_mask, vreg, vmask,	\
			   vstep, vmin, vmax) {				\
	.name			= #_id,					\
	.of_match		= of_name,				\
	.regulators_node	= "regulators",				\
	.ops			= &sc2730_regu_linear_ops,		\
	.type			= REGULATOR_VOLTAGE,			\
	.id			= SC2730_##_id,				\
	.owner			= THIS_MODULE,				\
	.min_uV			= vmin,					\
	.n_voltages		= ((vmax) - (vmin)) / (vstep) + 1,	\
	.uV_step		= vstep,				\
	.enable_is_inverted	= true,					\
	.enable_val		= 0,					\
	.enable_reg		= en_reg,				\
	.enable_mask		= en_mask,				\
	.vsel_reg		= vreg,					\
	.vsel_mask		= vmask,				\
	.linear_min_sel		= 0,					\
}

static const struct regulator_desc regulators[] = {
	SC2730_REGU_LINEAR(DCDC_CPU, "dcdc-cpu", SC2730_POWER_PD_SW,
			   SC2730_DCDC_CPU_PD_MASK, SC2730_DCDC_CPU_VOL,
			   SC2730_DCDC_CPU_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(DCDC_GPU, "dcdc-gpu", SC2730_POWER_PD_SW,
			   SC2730_DCDC_GPU_PD_MASK, SC2730_DCDC_GPU_VOL,
			   SC2730_DCDC_GPU_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(DCDC_CORE, "dcdc-core", SC2730_POWER_PD_SW,
			   SC2730_DCDC_CORE_PD_MASK, SC2730_DCDC_CORE_VOL,
			   SC2730_DCDC_CORE_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(DCDC_MODEM, "dcdc-modem", SC2730_POWER_PD_SW,
			   SC2730_DCDC_MODEM_PD_MASK, SC2730_DCDC_MODEM_VOL,
			   SC2730_DCDC_MODEM_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(DCDC_MEM, "dcdc-mem", SC2730_POWER_PD_SW,
			   SC2730_DCDC_MEM_PD_MASK, SC2730_DCDC_MEM_VOL,
			   SC2730_DCDC_MEM_VOL_MASK, 6250, 0, 1593750),
	SC2730_REGU_LINEAR(DCDC_MEMQ, "dcdc-memq", SC2730_POWER_PD_SW,
			   SC2730_DCDC_MEMQ_PD_MASK, SC2730_DCDC_MEMQ_VOL,
			   SC2730_DCDC_MEMQ_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(DCDC_GEN0, "dcdc-gen0", SC2730_POWER_PD_SW,
			   SC2730_DCDC_GEN0_PD_MASK, SC2730_DCDC_GEN0_VOL,
			   SC2730_DCDC_GEN0_VOL_MASK, 9375, 20000, 2410625),
	SC2730_REGU_LINEAR(DCDC_GEN1, "dcdc-gen1", SC2730_POWER_PD_SW,
			   SC2730_DCDC_GEN1_PD_MASK, SC2730_DCDC_GEN1_VOL,
			   SC2730_DCDC_GEN1_VOL_MASK, 6250, 50000, 1643750),
	SC2730_REGU_LINEAR(DCDC_SRAM, "dcdc-sram", SC2730_POWER_PD_SW,
			   SC2730_DCDC_SRAM_PD_MASK, SC2730_DCDC_SRAM_VOL,
			   SC2730_DCDC_SRAM_VOL_MASK, 3125, 0, 1596875),
	SC2730_REGU_LINEAR(LDO_AVDD18, "ldo-avdd18", SC2730_POWER_PD_SW,
			   SC2730_LDO_AVDD18_PD_MASK, SC2730_LDO_AVDD18_VOL,
			   SC2730_LDO_AVDD18_VOL_MASK, 10000, 1175000, 1805000),
	SC2730_REGU_LINEAR(LDO_VDDRF18, "ldo-vddrf18", SC2730_LDO_VDDRF18_PD,
			   SC2730_LDO_VDDRF18_PD_MASK, SC2730_LDO_VDDRF18_VOL,
			   SC2730_LDO_VDDRF18_VOL_MASK, 10000, 1175000, 1805000),
	SC2730_REGU_LINEAR(LDO_VDDCAMIO, "ldo-vddcamio", SC2730_LDO_VDDCAMIO_PD,
			   SC2730_LDO_VDDCAMIO_PD_MASK, SC2730_LDO_VDDCAMIO_VOL,
			   SC2730_LDO_VDDCAMIO_VOL_MASK, 10000, 1200000, 1830000),
	SC2730_REGU_LINEAR(LDO_VDDWCN, "ldo-vddwcn", SC2730_LDO_VDDWCN_PD,
			   SC2730_LDO_VDDWCN_PD_MASK, SC2730_LDO_VDDWCN_VOL,
			   SC2730_LDO_VDDWCN_VOL_MASK, 15000, 900000, 1845000),
	SC2730_REGU_LINEAR(LDO_VDDCAMD1, "ldo-vddcamd1", SC2730_LDO_VDDCAMD1_PD,
			   SC2730_LDO_VDDCAMD1_PD_MASK, SC2730_LDO_VDDCAMD1_VOL,
			   SC2730_LDO_VDDCAMD1_VOL_MASK, 15000, 900000, 1365000),
	SC2730_REGU_LINEAR(LDO_VDDCAMD0, "ldo-vddcamd0", SC2730_LDO_VDDCAMD0_PD,
			   SC2730_LDO_VDDCAMD0_PD_MASK, SC2730_LDO_VDDCAMD0_VOL,
			   SC2730_LDO_VDDCAMD0_VOL_MASK, 15000, 900000, 1365000),
	SC2730_REGU_LINEAR(LDO_VDDRF1V25, "ldo-vddrf1v25", SC2730_LDO_VDDRF1V25_PD,
			   SC2730_LDO_VDDRF1V25_PD_MASK, SC2730_LDO_VDDRF1V25_VOL,
			   SC2730_LDO_VDDRF1V25_VOL_MASK, 15000, 900000, 1365000),
	SC2730_REGU_LINEAR(LDO_AVDD12, "ldo-avdd12", SC2730_LDO_AVDD12_PD,
			   SC2730_LDO_AVDD12_PD_MASK, SC2730_LDO_AVDD12_VOL,
			   SC2730_LDO_AVDD12_VOL_MASK, 15000, 900000, 1365000),
	SC2730_REGU_LINEAR(LDO_VDDCAMA0, "ldo-vddcama0", SC2730_LDO_VDDCAMA0_PD,
			   SC2730_LDO_VDDCAMA0_PD_MASK, SC2730_LDO_VDDCAMA0_VOL,
			   SC2730_LDO_VDDCAMA0_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDCAMA1, "ldo-vddcama1", SC2730_LDO_VDDCAMA1_PD,
			   SC2730_LDO_VDDCAMA1_PD_MASK, SC2730_LDO_VDDCAMA1_VOL,
			   SC2730_LDO_VDDCAMA1_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDCAMMOT, "ldo-vddcammot", SC2730_LDO_VDDCAMMOT_PD,
			   SC2730_LDO_VDDCAMMOT_PD_MASK, SC2730_LDO_VDDCAMMOT_VOL,
			   SC2730_LDO_VDDCAMMOT_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDSIM2, "ldo-vddsim2", SC2730_LDO_VDDSIM2_PD,
			   SC2730_LDO_VDDSIM2_PD_MASK, SC2730_LDO_VDDSIM2_VOL,
			   SC2730_LDO_VDDSIM2_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDEMMCCORE, "ldo-vddemmccore", SC2730_LDO_VDDEMMCCORE_PD,
			   SC2730_LDO_VDDEMMCCORE_PD_MASK, SC2730_LDO_VDDEMMCCORE_VOL,
			   SC2730_LDO_VDDEMMCCORE_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDSDCORE, "ldo-vddsdcore", SC2730_LDO_VDDSDCORE_PD,
			   SC2730_LDO_VDDSDCORE_PD_MASK, SC2730_LDO_VDDSDCORE_VOL,
			   SC2730_LDO_VDDSDCORE_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDSDIO, "ldo-vddsdio", SC2730_LDO_VDDSDIO_PD,
			   SC2730_LDO_VDDSDIO_PD_MASK, SC2730_LDO_VDDSDIO_VOL,
			   SC2730_LDO_VDDSDIO_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDD28, "ldo-vdd28", SC2730_POWER_PD_SW,
			   SC2730_LDO_VDD28_PD_MASK, SC2730_LDO_VDD28_VOL,
			   SC2730_LDO_VDD28_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDWIFIPA, "ldo-vddwifipa", SC2730_LDO_VDDWIFIPA_PD,
			   SC2730_LDO_VDDWIFIPA_PD_MASK, SC2730_LDO_VDDWIFIPA_VOL,
			   SC2730_LDO_VDDWIFIPA_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDD18_DCXO, "ldo-vdd18-dcxo", SC2730_POWER_PD_SW,
			   SC2730_LDO_VDD18_DCXO_PD_MASK, SC2730_LDO_VDD18_DCXO_VOL,
			   SC2730_LDO_VDD18_DCXO_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDUSB33, "ldo-vddusb33", SC2730_LDO_VDDUSB33_PD,
			   SC2730_LDO_VDDUSB33_PD_MASK, SC2730_LDO_VDDUSB33_VOL,
			   SC2730_LDO_VDDUSB33_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDLDO0, "ldo-vddldo0", SC2730_LDO_VDDLDO0_PD,
			   SC2730_LDO_VDDLDO0_PD_MASK, SC2730_LDO_VDDLDO0_VOL,
			   SC2730_LDO_VDDLDO0_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDLDO1, "ldo-vddldo1", SC2730_LDO_VDDLDO1_PD,
			   SC2730_LDO_VDDLDO1_PD_MASK, SC2730_LDO_VDDLDO1_VOL,
			   SC2730_LDO_VDDLDO1_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDLDO2, "ldo-vddldo2", SC2730_LDO_VDDLDO2_PD,
			   SC2730_LDO_VDDLDO2_PD_MASK, SC2730_LDO_VDDLDO2_VOL,
			   SC2730_LDO_VDDLDO2_VOL_MASK, 10000, 1200000, 3750000),
	SC2730_REGU_LINEAR(LDO_VDDKPLED, "ldo-vddkpled", SC2730_LDO_VDDKPLED_PD,
			   SC2730_LDO_VDDKPLED_PD_MASK, SC2730_LDO_VDDKPLED_VOL,
			   SC2730_LDO_VDDKPLED_VOL_MASK, 10000, 1200000, 3750000),
};

static int sc2730_regulator_unlock(struct regmap *regmap)
{
	return regmap_write(regmap, SC2730_PWR_WR_PROT, SC2730_WR_UNLOCK_VALUE);
}

static int sc2730_regulator_probe(struct platform_device *pdev)
{
	int i, ret;
	struct regmap *regmap;
	struct regulator_config config = { };
	struct regulator_dev *rdev;

	regmap = dev_get_regmap(pdev->dev.parent, NULL);
	if (!regmap)
		return dev_err_probe(&pdev->dev, -ENODEV, "failed to get regmap\n");

	ret = sc2730_regulator_unlock(regmap);
	if (ret)
		return dev_err_probe(&pdev->dev, ret, "failed to release regulator lock\n");

	config.dev = pdev->dev.parent;
	config.regmap = regmap;

	for (i = 0; i < ARRAY_SIZE(regulators); i++) {
		rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config);
		if (IS_ERR(rdev)) {
			return dev_err_probe(&pdev->dev, PTR_ERR(rdev),
					     "failed to register regulator %s\n",
					     regulators[i].name);
		}
	}

	return 0;
}

static const struct platform_device_id sc2730_regulator_id_table[] = {
	{ .name = "sc2730-regulator" },
	{ }
};
MODULE_DEVICE_TABLE(platform, sc2730_regulator_id_table);

static struct platform_driver sc2730_regulator_driver = {
	.driver = {
		.name = "sc2730-regulator",
		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
	},
	.probe = sc2730_regulator_probe,
	.id_table = sc2730_regulator_id_table,
};

module_platform_driver(sc2730_regulator_driver);

MODULE_AUTHOR("Zhongfa Wang <zhongfa.wang@unisoc.com>");
MODULE_DESCRIPTION("Spreadtrum SC2730 regulator driver");
MODULE_LICENSE("GPL");
