/*
 * pmu.c
 *
 * Copyright (C) 2022  Sylvain Munaut <tnt@246tNt.com>
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include "config.h"
#include "pmu.h"


struct wb_pmu {
	uint32_t ctrl;
	uint32_t stat;
} __attribute__((packed,aligned(4)));

#define PMU_CTRL_BOOT_EXEC	(1 << 18)
#define PMU_CTRL_BOOT_SEL(n)	(((n) & 3) << 16)
#define PMU_CTRL_SYS_SHUTDOWN	(1 << 11)
#define PMU_CTRL_SYS_SUSPEND	(1 << 10)
#define PMU_CTRL_USB_OFF	(1 <<  9)
#define PMU_CTRL_USB_ON		(1 <<  8)
#define PMU_CTRL_BTN2_ACK       (1 <<  3)
#define PMU_CTRL_BTN1_ACK       (1 <<  1)

#define PMU_STAT_USB		(1 <<  8)
#define PMU_STAT_VBUS		(1 <<  5)
#define PMU_STAT_CHARGING	(1 <<  4)
#define PMU_STAT_BTN2_EVT	(1 <<  3)
#define PMU_STAT_BTN2_LONG	(1 <<  2)
#define PMU_STAT_BTN1_EVT	(1 <<  1)
#define PMU_STAT_BTN1_LONG	(1 <<  0)


static volatile struct wb_pmu * const pmu_regs= (void*)(PMU_BASE);


void
pmu_sys_suspend(void)
{
	pmu_regs->ctrl = PMU_CTRL_SYS_SUSPEND;
}

void
pmu_sys_shutdown(void)
{
	pmu_regs->ctrl = PMU_CTRL_SYS_SHUTDOWN;
}

void
pmu_sys_reboot(int n)
{
	pmu_regs->ctrl = PMU_CTRL_BOOT_SEL(n) | PMU_CTRL_BOOT_EXEC;
}


void
pmu_usb_enable(void)
{
	pmu_regs->ctrl = PMU_CTRL_USB_ON;
}

void
pmu_usb_disable(void)
{
	pmu_regs->ctrl = PMU_CTRL_USB_OFF;
}

void
pmu_usb_set_state(bool state)
{
	pmu_regs->ctrl = state ? PMU_CTRL_USB_ON : PMU_CTRL_USB_OFF;
}

bool
pmu_usb_state(void)
{
	return !!(pmu_regs->stat & PMU_STAT_USB);
}


bool
pmu_is_charging(void)
{
	return !!(pmu_regs->stat & PMU_STAT_CHARGING);
}

bool
pmu_is_vbus_present(void)
{
	return !!(pmu_regs->stat & PMU_STAT_VBUS);
}


uint8_t
pmu_get_buttons(void)
{
	uint32_t btn;
	uint32_t ack = 0;
	uint8_t  rv = 0;

	/* Read status */
	btn = pmu_regs->stat;

	/* Translate to events */
	if (btn & PMU_STAT_BTN1_EVT) {
		rv |= (btn & PMU_STAT_BTN1_LONG) ? BTN_1_LONG : BTN_1_SHORT;
		ack |= PMU_CTRL_BTN1_ACK;
	}

	if (btn & PMU_STAT_BTN2_EVT) {
		rv |= (btn & PMU_STAT_BTN2_LONG) ? BTN_2_LONG : BTN_2_SHORT;
		ack |= PMU_CTRL_BTN2_ACK;
	}

	/* Ack */
	if (ack)
		pmu_regs->ctrl = ack;

	return rv;
}
