/* SPDX-License-Identifier: GPL-2.0 */ /* * GXP power management. * * Copyright (C) 2021 Google LLC */ #ifndef __GXP_PM_H__ #define __GXP_PM_H__ #include #include "gxp-internal.h" #define AUR_DVFS_MIN_RATE 178000 static const uint aur_power_state2rate[] = { 0, /* AUR_OFF */ 178000, /* AUR_UUD */ 373000, /* AUR_SUD */ 750000, /* AUR_UD */ 1160000, /* AUR_NOM */ 178000, /* AUR_READY */ 268000, /* AUR_UUD_PLUS */ 560000, /* AUR_SUD_PLUS */ 975000, /* AUR_UD_PLUS */ }; enum aur_power_state { AUR_OFF = 0, AUR_UUD = 1, AUR_SUD = 2, AUR_UD = 3, AUR_NOM = 4, AUR_READY = 5, AUR_UUD_PLUS = 6, AUR_SUD_PLUS = 7, AUR_UD_PLUS = 8, }; enum aur_memory_power_state { AUR_MEM_UNDEFINED = 0, AUR_MEM_MIN = 1, AUR_MEM_VERY_LOW = 2, AUR_MEM_LOW = 3, AUR_MEM_HIGH = 4, AUR_MEM_VERY_HIGH = 5, AUR_MEM_MAX = 6, }; enum aur_power_cmu_mux_state { AUR_CMU_MUX_LOW = 0, AUR_CMU_MUX_NORMAL = 1, }; #define AUR_NUM_POWER_STATE (AUR_MAX_ALLOW_STATE + 1) #define AUR_NUM_MEMORY_POWER_STATE (AUR_MAX_ALLOW_MEMORY_STATE + 1) #define AUR_INIT_DVFS_STATE AUR_UUD /* * These macros mean the maximum valid enum value of aur_power_state and * aur_memory_power_state, not necessarily the state with the maximum power * level. */ #define AUR_MAX_ALLOW_STATE AUR_UD_PLUS #define AUR_MAX_ALLOW_MEMORY_STATE AUR_MEM_MAX #define AUR_NUM_POWER_STATE_WORKER 16 struct gxp_pm_device_ops { int (*pre_blk_powerup)(struct gxp_dev *gxp); int (*post_blk_powerup)(struct gxp_dev *gxp); int (*pre_blk_poweroff)(struct gxp_dev *gxp); int (*post_blk_poweroff)(struct gxp_dev *gxp); }; struct gxp_set_acpm_state_work { struct work_struct work; struct gxp_dev *gxp; unsigned long state; unsigned long prev_state; bool low_clkmux; bool prev_low_clkmux; bool using; }; struct gxp_req_pm_qos_work { struct work_struct work; struct gxp_dev *gxp; s32 int_val; s32 mif_val; bool using; }; struct gxp_power_manager { struct gxp_dev *gxp; struct mutex pm_lock; uint pwr_state_req_count[AUR_NUM_POWER_STATE]; uint low_clkmux_pwr_state_req_count[AUR_NUM_POWER_STATE]; uint mem_pwr_state_req_count[AUR_NUM_MEMORY_POWER_STATE]; /* * Last set CLKMUX state by asynchronous request handler. * If a core is booting, we shouldn't change clock mux state. This is * the expected state to set after all cores booting are finished. * Otherwise, it's the real state of CLKMUX. */ bool curr_low_clkmux; /* Last requested clock mux state */ bool last_scheduled_low_clkmux; int curr_state; int curr_memory_state; struct gxp_pm_device_ops *ops; struct gxp_set_acpm_state_work set_acpm_state_work[AUR_NUM_POWER_STATE_WORKER]; /* Serializes searching for an open worker in set_acpm_state_work[] */ struct mutex set_acpm_state_work_lock; struct gxp_req_pm_qos_work req_pm_qos_work[AUR_NUM_POWER_STATE_WORKER]; /* Serializes searching for an open worker in req_pm_qos_work[] */ struct mutex req_pm_qos_work_lock; struct workqueue_struct *wq; /* INT/MIF requests for memory bandwidth */ struct exynos_pm_qos_request int_min; struct exynos_pm_qos_request mif_min; int force_mux_normal_count; /* Max frequency that the thermal driver/ACPM will allow in Hz */ unsigned long thermal_limit; u64 blk_switch_count; }; /** * gxp_pm_blk_on() - Turn on the power for BLK_AUR * @gxp: The GXP device to turn on * * Return: * * 0 - BLK ON successfully * * -ENODEV - Cannot find PM interface */ int gxp_pm_blk_on(struct gxp_dev *gxp); /** * gxp_pm_blk_off() - Turn off the power for BLK_AUR * @gxp: The GXP device to turn off * * Return: * * 0 - BLK OFF successfully * * -ENODEV - Cannot find PM interface * * -EBUSY - Wakelock is held, blk is still busy */ int gxp_pm_blk_off(struct gxp_dev *gxp); /** * gxp_pm_get_blk_state() - Get the blk power state * @gxp: The GXP device to sample state * * Return: * * state - State number represented in kHZ, or 0 if OFF */ int gxp_pm_get_blk_state(struct gxp_dev *gxp); /** * gxp_pm_get_blk_switch_count() - Get the blk switch count number * @gxp: The GXP device to switch the blk * * Return: * * count - Switch count number after the module initialization. */ int gxp_pm_get_blk_switch_count(struct gxp_dev *gxp); /** * gxp_pm_core_on() - Turn on a core on GXP device * @gxp: The GXP device to operate * @core: The core ID to turn on * @verbose: A boolean flag to indicate whether to print the log * * Return: * * 0 - Core on process finished successfully * * -ETIMEDOUT - Core on process timed-out. */ int gxp_pm_core_on(struct gxp_dev *gxp, uint core, bool verbose); /** * gxp_pm_core_off() - Turn off a core on GXP device * @gxp: The GXP device to operate * @core: The core ID to turn off * * Return: * * 0 - Core off process finished successfully */ int gxp_pm_core_off(struct gxp_dev *gxp, uint core); /** * gxp_pm_init() - API for initialize PM interface for GXP, should only be * called once per probe * @gxp: The GXP device to operate * * Return: * * 0 - Initialization finished successfully * * -ENOMEM - Cannot get memory to finish init. */ int gxp_pm_init(struct gxp_dev *gxp); /** * gxp_pm_destroy() - API for removing * the power management interface * @gxp: The GXP device to operate * * Return: * * 0 - Remove finished successfully */ int gxp_pm_destroy(struct gxp_dev *gxp); /** * gxp_pm_blk_set_rate_acpm() - API for setting the block-level DVFS rate. * This function can be called at any point after block power on. * @gxp: The GXP device to operate * @rate: Rate number in khz that need to be set. * Supported rate is in aur_power_state2rate, * if experiment is needed for unsupported rate * please refer to Lassen's ECT table. * * Return: * * 0 - Set finished successfully * * Other - Set rate encounter issue in exynos_acpm_set_rate */ int gxp_pm_blk_set_rate_acpm(struct gxp_dev *gxp, unsigned long rate); /** * gxp_pm_blk_get_state_acpm() - API for getting * the current DVFS state of the Aurora block. * @gxp: The GXP device to operate * * Return: * * State - State number in Khz from ACPM */ int gxp_pm_blk_get_state_acpm(struct gxp_dev *gxp); /** * gxp_pm_update_requested_power_states() - API for a GXP client to vote for a * requested power state and a requested memory power state. * @gxp: The GXP device to operate. * @origin_state: An existing old requested state, will be cleared. If this is * the first vote, pass AUR_OFF. * @origin_requested_low_clkmux: Specify whether the existing vote was requested with * low frequency CLKMUX flag. * @requested_state: The new requested state. * @requested_low_clkmux: Specify whether the new vote is requested with low frequency * CLKMUX flag. Will take no effect if the @requested state is * AUR_OFF. * @origin_mem_state: An existing old requested state, will be cleared. If this is * the first vote, pass AUR_MEM_UNDEFINED. * @requested_mem_state: The new requested state. * * Return: * * 0 - Voting registered * * -EINVAL - Invalid original state or requested state */ int gxp_pm_update_requested_power_states( struct gxp_dev *gxp, enum aur_power_state origin_state, bool origin_requested_low_clkmux, enum aur_power_state requested_state, bool requested_low_clkmux, enum aur_memory_power_state origin_mem_state, enum aur_memory_power_state requested_mem_state); /* * gxp_pm_force_clkmux_normal() - Force PLL_CON0_NOC_USER and PLL_CON0_PLL_AUR MUX * switch to the normal state. This is required to guarantee LPM works when the core * is starting the firmware. */ void gxp_pm_force_clkmux_normal(struct gxp_dev *gxp); /* * gxp_pm_resume_clkmux() - Check PLL_CON0_NOC_USER and PLL_CON0_PLL_AUR MUX state * modified by gxp_pm_force_clkmux_normal(). If the current vote is requested with low * frequency CLKMUX flag, should set the MUX state to AUR_CMU_MUX_LOW. */ void gxp_pm_resume_clkmux(struct gxp_dev *gxp); /** * gxp_pm_set_thermal_limit() - Notify the power manager of a thermal limit * @gxp: The GXP device the limit is set for * @thermal_limit: The highest frequency, in Hz, the thermal limit allows * * The power management code will only use this information for logging. */ void gxp_pm_set_thermal_limit(struct gxp_dev *gxp, unsigned long thermal_limit); #endif /* __GXP_PM_H__ */