// SPDX-License-Identifier: GPL-2.0+
/*
 * AXP PMIC SPL driver
 * (C) Copyright 2024 Arm Ltd.
 */

#include <errno.h>
#include <linux/types.h>
#include <asm/arch/pmic_bus.h>
#include <axp_pmic.h>

struct axp_reg_desc_spl {
	u8	enable_reg;
	u8	enable_mask;
	u8	volt_reg;
	u8	volt_mask;
	u16	min_mV;
	u16	max_mV;
	u8	step_mV;
	u8	split;
};

#define NA 0xff

#if defined(CONFIG_AXP717_POWER)				/* AXP717 */

static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
	{ 0x80, BIT(0), 0x83, 0x7f,  500, 1540,  10, 70 },
	{ 0x80, BIT(1), 0x84, 0x7f,  500, 1540,  10, 70 },
	{ 0x80, BIT(2), 0x85, 0x7f,  500, 1840,  10, 70 },
};

#define AXP_CHIP_VERSION	0x0
#define AXP_CHIP_VERSION_MASK	0x0
#define AXP_CHIP_ID		0x0
#define AXP_SHUTDOWN_REG	0x27
#define AXP_SHUTDOWN_MASK	BIT(0)

#elif defined(CONFIG_AXP803_POWER)				/* AXP803 */

static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
	{ 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
	{ 0x10, BIT(1), 0x21, 0x7f,  500, 1300,  10, 70 },
	{ 0x10, BIT(2), 0x22, 0x7f,  500, 1300,  10, 70 },
	{ 0x10, BIT(3), 0x23, 0x7f,  500, 1300,  10, 70 },
	{ 0x10, BIT(4), 0x24, 0x7f,  800, 1840,  10, 32 },
	{ 0x10, BIT(5), 0x25, 0x7f,  600, 1520,  10, 50 },
};

#define AXP_CHIP_VERSION	0x3
#define AXP_CHIP_VERSION_MASK	0xcf
#define AXP_CHIP_ID		0x41
#define AXP_SHUTDOWN_REG	0x32
#define AXP_SHUTDOWN_MASK	BIT(7)

#elif defined(CONFIG_AXP313_POWER)				/* AXP313 */

static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
	{ 0x10, BIT(0), 0x13, 0x7f,  500, 1540,  10, 70 },
	{ 0x10, BIT(1), 0x14, 0x7f,  500, 1540,  10, 70 },
	{ 0x10, BIT(2), 0x15, 0x7f,  500, 1840,  10, 70 },
};

#define AXP_CHIP_VERSION	0x3
#define AXP_CHIP_VERSION_MASK	0xc8
#define AXP_CHIP_ID		0x48
#define AXP_SHUTDOWN_REG	0x1a
#define AXP_SHUTDOWN_MASK	BIT(7)

#elif defined(CONFIG_AXP305_POWER)				/* AXP305 */

static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
	{ 0x10, BIT(0), 0x12, 0x7f,  600, 1520,  10, 50 },
	{ 0x10, BIT(1), 0x13, 0x1f, 1000, 2550,  50, NA },
	{ 0x10, BIT(2), 0x14, 0x7f,  600, 1520,  10, 50 },
	{ 0x10, BIT(3), 0x15, 0x3f,  600, 1500,  20, NA },
	{ 0x10, BIT(4), 0x16, 0x1f, 1100, 3400, 100, NA },
};

#define AXP_CHIP_VERSION	0x3
#define AXP_CHIP_VERSION_MASK	0xcf
#define AXP_CHIP_ID		0x40
#define AXP_SHUTDOWN_REG	0x32
#define AXP_SHUTDOWN_MASK	BIT(7)

#else

	#error "Please define the regulator registers in axp_spl_regulators[]."

#endif

static u8 axp_mvolt_to_cfg(int mvolt, const struct axp_reg_desc_spl *reg)
{
	if (mvolt < reg->min_mV)
		mvolt = reg->min_mV;
	else if (mvolt > reg->max_mV)
		mvolt = reg->max_mV;

	mvolt -= reg->min_mV;

	/* voltage in the first range ? */
	if (mvolt <= reg->split * reg->step_mV)
		return mvolt / reg->step_mV;

	mvolt -= reg->split * reg->step_mV;

	return reg->split + mvolt / (reg->step_mV * 2);
}

static int axp_set_dcdc(int dcdc_num, unsigned int mvolt)
{
	const struct axp_reg_desc_spl *reg;
	int ret;

	if (dcdc_num < 1 || dcdc_num > ARRAY_SIZE(axp_spl_dcdc_regulators))
		return -EINVAL;

	reg = &axp_spl_dcdc_regulators[dcdc_num - 1];

	if (mvolt == 0)
		return pmic_bus_clrbits(reg->enable_reg, reg->enable_mask);

	ret = pmic_bus_write(reg->volt_reg, axp_mvolt_to_cfg(mvolt, reg));
	if (ret)
		return ret;

	return pmic_bus_setbits(reg->enable_reg, reg->enable_mask);
}

int axp_set_dcdc1(unsigned int mvolt)
{
	return axp_set_dcdc(1, mvolt);
}

int axp_set_dcdc2(unsigned int mvolt)
{
	return axp_set_dcdc(2, mvolt);
}

int axp_set_dcdc3(unsigned int mvolt)
{
	return axp_set_dcdc(3, mvolt);
}

int axp_set_dcdc4(unsigned int mvolt)
{
	return axp_set_dcdc(4, mvolt);
}

int axp_set_dcdc5(unsigned int mvolt)
{
	return axp_set_dcdc(5, mvolt);
}

int axp_init(void)
{
	int ret = pmic_bus_init();

	if (ret)
		return ret;

	if (AXP_CHIP_VERSION_MASK) {
		u8 axp_chip_id;

		ret = pmic_bus_read(AXP_CHIP_VERSION, &axp_chip_id);
		if (ret)
			return ret;

		if ((axp_chip_id & AXP_CHIP_VERSION_MASK) != AXP_CHIP_ID) {
			debug("unknown PMIC: 0x%x\n", axp_chip_id);
			return -EINVAL;
		}
	}

	return 0;
}

#if !CONFIG_IS_ENABLED(ARM_PSCI_FW) && !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	pmic_bus_setbits(AXP_SHUTDOWN_REG, AXP_SHUTDOWN_MASK);

	/* infinite loop during shutdown */
	while (1)
		;

	/* not reached */
	return 0;
}
#endif
