/*
 * Copyright (c) 2025-2026 Texas Instruments Incorporated - https://www.ti.com
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*
 * PSC Driver API
 *
 * This header provides the Power and Sleep Controller (PSC) driver interface
 * including data structures for power domains and LPSC modules, state
 * management functions, reset control, and module dependency handling.
 */

#ifndef TI_PSC_H
#define TI_PSC_H

#include <ti_device.h>
#include <ti_pm_types.h>

/*
 * Indicates that no device is mapped to this PSC module
 *
 * Used in device-to-PSC mapping tables to indicate that a PSC module
 * has no associated device.
 */
#define TI_PSC_DEV_NONE		7U

/*
 * Indicates that multiple devices are mapped to this PSC module
 *
 * Used in device-to-PSC mapping tables to indicate that a PSC module
 * controls multiple devices. The actual device list is stored elsewhere.
 */
#define TI_PSC_DEV_MULTIPLE	6U

/*
 * Flag indicating that the power domain exists and is valid
 *
 * Set in ti_psc_pd_data.flags to indicate that this power domain is
 * present and the information in the record is valid.
 */
#define TI_PSC_PD_EXISTS		BIT(0)

/*
 * Flag indicating that the power domain is always on
 *
 * Set in ti_psc_pd_data.flags to indicate that this power domain cannot
 * be powered down and is always active.
 */
#define TI_PSC_PD_ALWAYSON		BIT(1)

/*
 * Flag indicating that the power domain has dependencies
 *
 * Set in ti_psc_pd_data.flags to indicate that this power domain depends
 * on another power domain being active. The dependency is specified in
 * the depends field.
 */
#define TI_PSC_PD_DEPENDS		BIT(2)

/*
 * PSC power domain constant data
 *
 * This structure contains the constant configuration data for a PSC power
 * domain, including its characteristics, dependencies, and clock requirements.
 * This data is typically stored in ROM or const data sections.
 */
struct ti_psc_pd_data {
	/*
	 * Power domain configuration flags
	 *
	 * Combination of TI_PSC_PD_EXISTS, TI_PSC_PD_ALWAYSON, and TI_PSC_PD_DEPENDS
	 * flags indicating the power domain's characteristics.
	 */
	uint8_t flags;

	/*
	 * Index of the power domain this one depends on
	 *
	 * If TI_PSC_PD_DEPENDS is set in flags, this field contains the ti_pd_idx_t
	 * of the power domain that must be active before this one can be enabled.
	 */
	uint8_t depends;

	/*
	 * Clock dependencies for power domain transitions
	 *
	 * Some domains need a clock running in order to transition. This
	 * is the id of that clock. If no such clock is needed, set to
	 * an invalid clock id (0). We currently support only 3 clocks.
	 */
	ti_clk_idx_t clock_dep[3];
};

/*
 * PSC power domain dynamic data
 *
 * This structure contains the runtime state information for a PSC power
 * domain, tracking its usage and power-up enablement status. This data
 * changes during system operation.
 */
struct ti_psc_pd {
	/*
	 * Reference count tracking power domain usage
	 *
	 * Number of modules or users currently requiring this power domain
	 * to be active. The domain can only be powered down when this
	 * count reaches zero.
	 */
	uint8_t use_count;

	/*
	 * Power-up enable status
	 *
	 * True if this power domain is enabled for power-up operations,
	 * false otherwise. This is used to track the requested state of
	 * the power domain.
	 */
	bool pwr_up_enabled;
};

/* Module is present, information in record is valid */
#define TI_LPSC_MODULE_EXISTS	BIT(0)

/* Module is not capable of clock gating */
#define TI_LPSC_NO_CLOCK_GATING	BIT(1)

/* Module depends on another module listed in depends field */
#define TI_LPSC_DEPENDS		BIT(2)

/* Module implements configurable reset isolation */
#define TI_LPSC_HAS_RESET_ISO	BIT(3)

/* Module implements a local reset */
#define TI_LPSC_HAS_LOCAL_RESET	BIT(4)

/* States that enable module reset are disallowed */
#define TI_LPSC_NO_MODULE_RESET	BIT(5)

/*
 * Set if the module data points to a dev_list, false it a dev_array is
 * embedded.
 */
#define TI_LPSC_DEVICES_LIST	BIT(6)

/*
 * LPSC module constant configuration data
 *
 * This structure contains the constant configuration data for a Local Power
 * Sleep Controller (LPSC) module, including device associations, dependencies,
 * clock requirements, and module characteristics. This data is typically stored
 * in ROM or const data sections and defines the static properties of each
 * LPSC module in the system.
 */
struct ti_lpsc_module_data {
	/*
	 * Device-to-LPSC module association
	 *
	 * Which devices are members of which PSC module is duplicated in the
	 * list of devices. However, it is duplicated here so that the entire
	 * list of devices does not need to be looped through to find that
	 * information. It could be generated dynamically but is stored in this
	 * way to save memory.
	 */
	union {
		/*
		 * Embedded device array for small device lists
		 *
		 * If there are 4 or less devices, they can be stored here,
		 * terminated by the 4th device or TI_DEV_ID_NONE. This avoids
		 * the need for a separate pointer and saves memory.
		 */
		ti_dev_idx_t dev_array[sizeof(void *) / sizeof(ti_dev_idx_t)];

		/*
		 * Pointer to external device list for large device lists
		 *
		 * More than 4 devices must be stored in a separate list,
		 * terminated by TI_DEV_ID_NONE. The TI_LPSC_DEVICES_LIST flag must be
		 * set when this field is used instead of dev_array.
		 */
		const ti_dev_idx_t *dev_list;
	} lpsc_dev;

	/*
	 * Clock dependencies for module transitions
	 *
	 * Some modules need a clock running in order to transition. This
	 * is the id of that clock. If no such clock is needed, set to
	 * an invalid clock id (0). We currently support only one clock,
	 * this causes the structure size to be 8 bytes in the case of an
	 * 8 bit ti_clk_idx_t and 12 bytes for a 16 bit ti_clk_idx_t.
	 */
	ti_clk_idx_t clock_dep[1];

	/*
	 * Module configuration flags
	 *
	 * Combination of TI_LPSC_MODULE_EXISTS, TI_LPSC_NO_CLOCK_GATING, TI_LPSC_DEPENDS,
	 * TI_LPSC_HAS_RESET_ISO, TI_LPSC_HAS_LOCAL_RESET, TI_LPSC_NO_MODULE_RESET, and
	 * TI_LPSC_DEVICES_LIST flags indicating the module's characteristics and
	 * capabilities.
	 */
	uint8_t flags;

	/*
	 * Index of the LPSC module this one depends on
	 *
	 * If TI_LPSC_DEPENDS is set in flags, this field contains the ti_lpsc_idx_t
	 * of the LPSC module that must be active before this one can be enabled.
	 */
	ti_lpsc_idx_t depends;

	/*
	 * PSC index of the dependency module
	 *
	 * If TI_LPSC_DEPENDS is set in flags, this field contains the ti_psc_idx_t
	 * of the PSC that controls the dependency module. This allows for
	 * cross-PSC dependencies.
	 */
	uint8_t depends_psc_idx;

	/*
	 * Power domain index this module belongs to
	 *
	 * The ti_pd_idx_t of the power domain that controls this LPSC module.
	 * The module can only be active when its power domain is powered up.
	 */
	uint8_t powerdomain;
};

/*
 * LPSC module dynamic runtime data
 *
 * This structure contains the runtime state information for an LPSC module,
 * tracking its usage, power state, reset status, and loss count. This data
 * changes during system operation as modules are enabled, disabled, and reset.
 */
struct ti_lpsc_module {
	/*
	 * Module reset loss counter
	 *
	 * Incremented after module reset. This value is used to detect when
	 * a module has undergone a reset and lost its state, allowing drivers
	 * to know when reinitialization is necessary.
	 */
	uint32_t loss_count;

	/*
	 * Active usage reference count
	 *
	 * Non-zero if module should be active (clocks running). Incremented
	 * by ti_lpsc_module_get() and decremented by ti_lpsc_module_put().
	 * The module transitions to enabled state when this count is non-zero.
	 */
	uint8_t use_count;

	/*
	 * Retention reference count
	 *
	 * Non-zero if module should be powered-up but may be clock-gated.
	 * Incremented by ti_lpsc_module_ret_get() and decremented by
	 * ti_lpsc_module_ret_put(). This allows the module to retain state
	 * while saving power by stopping clocks.
	 */
	uint8_t ret_count;

	/*
	 * Current programmed software state
	 *
	 * Current programmed state of the module (MDSTAT_STATE_[...]).
	 * Reflects the state written to hardware and may differ from the
	 * actual hardware state during transitions.
	 */
	uint8_t sw_state;

	/*
	 * Module reset retention flag
	 *
	 * True if the module is forced on due to a module reset. In this
	 * case sw_state indicates SWRSTDISABLE but this module holds
	 * a reference count to its powerdomain. This prevents the power
	 * domain from being disabled while a reset is active.
	 */
	bool sw_mrst_ret;

	/*
	 * Power-up enable status
	 *
	 * Non-zero if the module power-up has been enabled. This tracks
	 * whether the module is currently in a power-up enabled state.
	 */
	uint8_t pwr_up_enabled;

	/*
	 * Power-up retention status
	 *
	 * Non-zero if the module is in power-up retention mode. This allows
	 * the module to maintain state while in a low-power configuration.
	 */
	uint8_t pwr_up_ret;

	/*
	 * Module reset active flag
	 *
	 * True if host has requested a module reset. This indicates that
	 * a reset operation is currently active or pending for this module.
	 */
	uint8_t mrst_active;
};

/*
 * PSC driver operations structure
 *
 * Global constant structure containing the PSC driver operations and
 * callbacks. This structure implements the ti_drv interface for PSC
 * devices and is used by the device management framework to interact
 * with PSC hardware.
 */
extern const struct ti_drv psc_drv;

struct ti_soc_device_data;

/*
 * Array of devices controlled by multiple PSC modules
 *
 * Global constant array containing pointers to device data structures for
 * devices that are controlled by multiple PSC modules. This allows the
 * system to handle complex device-to-PSC mappings where a single device
 * may require coordination across multiple PSC modules. The array is
 * terminated by a NULL pointer.
 */
extern const struct ti_soc_device_data *const soc_psc_multiple_domains[];

/*
 * Get the index of a power domain within its PSC
 *
 * Calculates the power domain index by determining the offset of the
 * power domain structure within the PSC's power domain array.
 */
ti_pd_idx_t ti_psc_pd_idx(struct ti_device *psc_dev, const struct ti_psc_pd *pd);

/**
 * ti_psc_pd_wait() - Wait for a power domain transition to complete
 * @psc_dev: The PSC device that controls this power domain.
 * @pd: Pointer to the power domain to wait for.
 *
 * Polls the hardware status registers until the power domain transition
 * completes. This function blocks until the power domain reaches its
 * target state.
 */
void ti_psc_pd_wait(struct ti_device *psc_dev, struct ti_psc_pd *pd);

/**
 * ti_psc_pd_get() - Increment power domain reference count and enable if needed
 * @psc_dev: The PSC device that controls this power domain.
 * @pd: Pointer to the power domain to enable.
 *
 * Increments the use_count for the power domain and powers it up if this
 * is the first reference. Also handles dependency power domains recursively.
 */
void ti_psc_pd_get(struct ti_device *psc_dev, struct ti_psc_pd *pd);

/**
 * ti_psc_pd_put() - Decrement power domain reference count and disable if unused
 * @psc_dev: The PSC device that controls this power domain.
 * @pd: Pointer to the power domain to potentially disable.
 *
 * Decrements the use_count for the power domain and powers it down if
 * the count reaches zero. Also handles dependency power domains recursively.
 */
void ti_psc_pd_put(struct ti_device *psc_dev, struct ti_psc_pd *pd);

/*
 * Get the index of an LPSC module within its PSC
 *
 * Calculates the LPSC module index by determining the offset of the
 * module structure within the PSC's module array.
 */
ti_lpsc_idx_t ti_lpsc_module_idx(struct ti_device *psc_dev, const struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_get() - Increment LPSC module active reference count and enable
 * @psc_dev: The PSC device that controls this module.
 * @module: Pointer to the LPSC module to enable.
 *
 * Increments the use_count for the module and enables it (clocks running)
 * if this is the first reference. Also handles power domain and dependency
 * modules recursively. The module will be fully powered and clocked.
 */
void ti_lpsc_module_get(struct ti_device *psc_dev, struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_put() - Decrement LPSC module active reference count and disable if unused
 * @psc_dev: The PSC device that controls this module.
 * @module: Pointer to the LPSC module to potentially disable.
 *
 * Decrements the use_count for the module. If the count reaches zero and
 * ret_count is also zero, the module is disabled. Also handles power domain
 * and dependency modules recursively.
 */
void ti_lpsc_module_put(struct ti_device *psc_dev, struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_ret_get() - Increment LPSC module retention reference count
 * @psc_dev: The PSC device that controls this module.
 * @module: Pointer to the LPSC module to put in retention mode.
 *
 * Increments the ret_count for the module, ensuring it remains powered
 * but allowing clock gating. The module retains its state while saving
 * power. Also handles power domain and dependency modules recursively.
 */
void ti_lpsc_module_ret_get(struct ti_device *psc_dev, struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_ret_put() - Decrement LPSC module retention reference count
 * @psc_dev: The PSC device that controls this module.
 * @module: Pointer to the LPSC module to release from retention mode.
 *
 * Decrements the ret_count for the module. If both ret_count and use_count
 * reach zero, the module can be fully disabled. Also handles power domain
 * and dependency modules recursively.
 */
void ti_lpsc_module_ret_put(struct ti_device *psc_dev, struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_wait() - Wait for an LPSC module transition to complete
 * @psc_dev: The PSC device that controls this module.
 * @module: Pointer to the LPSC module to wait for.
 *
 * Polls the hardware status registers until the module transition completes.
 * This function blocks until the module reaches its target state.
 */
void ti_lpsc_module_wait(struct ti_device *psc_dev, struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_set_reset_iso() - Set the reset isolation flag for a PSC module
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to modify.
 * @enable: True to enable reset isolation, false to disable.
 *
 * This directly modifies the hardware state.
 */
void ti_lpsc_module_set_reset_iso(struct ti_device *psc_dev,
				  struct ti_lpsc_module *module, bool enable);

/**
 * ti_lpsc_module_get_reset_iso() - Get the reset isolation setting from a PSC module.
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to query.
 *
 * This queries the true hardware state.
 *
 * Return: True if reset isolation is enabled for this module, false if otherwise.
 */
bool ti_lpsc_module_get_reset_iso(struct ti_device *psc_dev,
				  struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_set_local_reset() - Set/clear the local reset state of a PSC module
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to modify.
 * @enable: True to enable local reset, false to release local reset.
 *
 * The function of the local reset is module specific and only available on
 * certain modules. The most common use is to hold processors (such as the ICSS
 * or DSP) in reset.
 */
void ti_lpsc_module_set_local_reset(struct ti_device *psc_dev,
				    struct ti_lpsc_module *module, bool enable);

/**
 * ti_lpsc_module_set_module_reset() - Set/clear the module reset state of a PSC module
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to modify.
 * @enable: True to enable module reset, false to release module reset.
 *
 * The function of the module reset is module specific and only available on
 * certain modules.
 */
void ti_lpsc_module_set_module_reset(struct ti_device *psc_dev,
				     struct ti_lpsc_module *module, bool enable);

/**
 * ti_lpsc_module_get_local_reset() - Get the local reset state from a PSC module.
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to query.
 *
 * This queries the true hardware state.
 *
 * Return: True if local reset is asserted for this module, false if otherwise.
 */
bool ti_lpsc_module_get_local_reset(struct ti_device *psc_dev,
				    struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_get_module_reset() - Get the module reset state from a PSC module.
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to query.
 *
 * This queries the true hardware state.
 *
 * Return: True if module reset is asserted for this module, false if otherwise.
 */
bool ti_lpsc_module_get_module_reset(struct ti_device *psc_dev,
				     const struct ti_lpsc_module *module);

/**
 * ti_lpsc_module_get_state() - Get the module state from a PSC module.
 * @psc_dev: The PSC device that controls this module.
 * @module: The PSC module to query.
 *
 * This queries the true hardware state.
 *
 * Return: 0 for a disabled module, 1 for an enabled module, 2 for a module in transition.
 */
uint32_t ti_lpsc_module_get_state(struct ti_device *psc_dev,
				  struct ti_lpsc_module *module);

/*
 * Look up a PSC device by its index
 *
 * Searches the system for the PSC device with the specified index.
 * This function is used to obtain a reference to a PSC device for
 * subsequent operations.
 */
struct ti_device *ti_psc_lookup(ti_psc_idx_t id);

/*
 * Look up a power domain within a PSC device
 *
 * Returns a pointer to the power domain structure for the specified
 * power domain index within the given PSC device.
 */
struct ti_psc_pd *ti_psc_lookup_pd(struct ti_device *psc_dev, ti_pd_idx_t id);

/*
 * Look up an LPSC module within a PSC device
 *
 * Returns a pointer to the LPSC module structure for the specified
 * module index within the given PSC device.
 */
struct ti_lpsc_module *ti_psc_lookup_lpsc(struct ti_device *psc_dev, ti_lpsc_idx_t id);

/**
 * ti_psc_drop_pwr_up_ref() - Drop all power-up references across all PSC modules and domains
 *
 * Clears the pwr_up_enabled flags for all power domains and modules across
 * all PSC devices in the system. This is typically called during system
 * shutdown or when transitioning to a low-power state to release all
 * power-up references and allow the system to power down completely.
 */
void ti_psc_drop_pwr_up_ref(void);

/*
 * PSC device dynamic runtime data
 *
 * This structure contains the runtime state information for a PSC device,
 * including tracking of enabled power domains and modules. This data
 * changes during system operation as power domains and modules are
 * enabled and disabled.
 */
struct ti_psc_data {
	/*
	 * Linked list pointer to next PSC device
	 *
	 * Used to maintain a linked list of all PSC devices in the system.
	 * NULL indicates the end of the list.
	 */
	struct ti_device *next;

	/*
	 * Bit field of currently enabled power domains
	 *
	 * Each bit represents a power domain within this PSC. A set bit
	 * indicates the power domain is currently enabled. Bit position
	 * corresponds to the power domain index.
	 */
	uint32_t pds_enabled;
};

/*
 * PSC driver data structure
 *
 * This structure contains all the data needed for a PSC driver instance,
 * including both constant configuration data and dynamic runtime data for
 * power domains and LPSC modules. Each PSC device in the system has an
 * associated ti_psc_drv_data structure that defines its complete state
 * and configuration.
 */
struct ti_psc_drv_data {
	/*
	 * Base driver data structure
	 *
	 * Contains common driver data fields required by the device management
	 * framework. This must be the first member to allow casting between
	 * ti_drv_data and ti_psc_drv_data pointers.
	 */
	struct ti_drv_data drv_data;

	/*
	 * Pointer to dynamic runtime data
	 *
	 * Points to the ti_psc_data structure containing runtime state
	 * information for this PSC device, including enabled power domains
	 * and modules.
	 */
	struct ti_psc_data *data;

	/*
	 * Pointer to constant power domain data table
	 *
	 * Array of ti_psc_pd_data structures containing the constant
	 * configuration for each power domain in this PSC. The array
	 * has pd_count elements indexed by ti_pd_idx_t.
	 */
	const struct ti_psc_pd_data *pd_data;

	/*
	 * Pointer to dynamic power domain data table
	 *
	 * Array of ti_psc_pd structures containing the runtime state
	 * for each power domain in this PSC. The array has pd_count
	 * elements indexed by ti_pd_idx_t.
	 */
	struct ti_psc_pd *powerdomains;

	/*
	 * Pointer to constant LPSC module data table
	 *
	 * Array of ti_lpsc_module_data structures containing the constant
	 * configuration for each LPSC module in this PSC. The array has
	 * module_count elements indexed by ti_lpsc_idx_t.
	 */
	const struct ti_lpsc_module_data *mod_data;

	/*
	 * Pointer to dynamic LPSC module data table
	 *
	 * Array of ti_lpsc_module structures containing the runtime state
	 * for each LPSC module in this PSC. The array has module_count
	 * elements indexed by ti_lpsc_idx_t.
	 */
	struct ti_lpsc_module *modules;

	/*
	 * Total number of power domains in this PSC
	 *
	 * Defines the size of the pd_data and powerdomains arrays. Valid
	 * power domain indices range from 0 to (pd_count - 1).
	 */
	ti_pd_idx_t pd_count;

	/*
	 * Total number of LPSC modules in this PSC
	 *
	 * Defines the size of the mod_data and modules arrays. Valid
	 * LPSC module indices range from 0 to (module_count - 1).
	 */
	ti_lpsc_idx_t module_count;

	/*
	 * Index of this PSC in the system
	 *
	 * Unique identifier for this PSC device within the system. Used
	 * for PSC lookup operations and cross-PSC dependencies.
	 */
	ti_psc_idx_t psc_idx;

	/*
	 * Number of supported sleep/power states
	 *
	 * The total number of sleep modes or low-power states that this
	 * PSC supports. Used for power management and system suspend/resume
	 * operations.
	 */
	uint8_t sleep_mode_count;

	/* PSC register base address */
	uint32_t base;
};

static inline const struct ti_psc_drv_data *ti_to_psc_drv_data(const struct ti_drv_data *data)
{
	return ti_container_of(data, const struct ti_psc_drv_data, drv_data);
}

#endif /* TI_PSC_H */
