diff options
Diffstat (limited to 'gxp-pm.c')
-rw-r--r-- | gxp-pm.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/gxp-pm.c b/gxp-pm.c new file mode 100644 index 0000000..cd478fb --- /dev/null +++ b/gxp-pm.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GXP power management. + * + * Copyright (C) 2021 Google LLC + */ + +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/refcount.h> +#include <linux/types.h> + +#ifdef CONFIG_GXP_CLOUDRIPPER +#include <linux/acpm_dvfs.h> +#endif + +#include "gxp-bpm.h" +#include "gxp-doorbell.h" +#include "gxp-internal.h" +#include "gxp-lpm.h" +#include "gxp-pm.h" + +static struct gxp_pm_device_ops gxp_aur_ops = { + .pre_blk_powerup = NULL, + .post_blk_powerup = NULL, + .pre_blk_poweroff = NULL, + .post_blk_poweroff = NULL, +}; + +static int gxp_pm_blkpwr_up(struct gxp_dev *gxp) +{ + int ret = 0; + +#ifdef CONFIG_GXP_CLOUDRIPPER + ret = pm_runtime_get_sync(gxp->dev); + if (ret) + dev_err(gxp->dev, "%s: pm_runtime_get_sync returned %d\n", + __func__, ret); +#endif + return ret; +} + +static int gxp_pm_blkpwr_down(struct gxp_dev *gxp) +{ + int ret = 0; + +#ifdef CONFIG_GXP_CLOUDRIPPER + /* + * Need to put TOP LPM into active state before blk off + * b/189396709 + */ + lpm_write_32_psm(gxp, LPM_TOP_PSM, LPM_REG_ENABLE_STATE_1, 0x0); + lpm_write_32_psm(gxp, LPM_TOP_PSM, LPM_REG_ENABLE_STATE_2, 0x0); + ret = pm_runtime_put_sync(gxp->dev); + if (ret) + dev_err(gxp->dev, "%s: pm_runtime_put_sync returned %d\n", + __func__, ret); +#endif + return ret; +} + +int gxp_pm_blk_set_state_acpm(struct gxp_dev *gxp, unsigned long state) +{ + int ret = 0; + +#ifdef CONFIG_GXP_CLOUDRIPPER + ret = exynos_acpm_set_rate(AUR_DVFS_DOMAIN, state); + dev_dbg(gxp->dev, "%s: state %lu, ret %d\n", __func__, state, ret); +#endif + return ret; +} + +int gxp_pm_blk_get_state_acpm(struct gxp_dev *gxp) +{ + int ret = 0; + +#ifdef CONFIG_GXP_CLOUDRIPPER + ret = exynos_acpm_get_rate(AUR_DVFS_DOMAIN, AUR_DEBUG_CORE_FREQ); + dev_dbg(gxp->dev, "%s: state %d\n", __func__, ret); +#endif + return ret; +} + +int gxp_pm_blk_on(struct gxp_dev *gxp) +{ + int ret = 0; + + if (WARN_ON(!gxp->power_mgr)) { + dev_err(gxp->dev, "%s: No PM found\n", __func__); + return -ENODEV; + } + + mutex_lock(&gxp->power_mgr->pm_lock); + ret = gxp_pm_blkpwr_up(gxp); + if (!ret) { + gxp_pm_blk_set_state_acpm(gxp, AUR_INIT_DVFS_STATE); + gxp->power_mgr->curr_state = AUR_INIT_DVFS_STATE; + } + mutex_unlock(&gxp->power_mgr->pm_lock); + + return ret; +} + +int gxp_pm_blk_off(struct gxp_dev *gxp) +{ + int ret = 0; + + if (WARN_ON(!gxp->power_mgr)) { + dev_err(gxp->dev, "%s: No PM found\n", __func__); + return -ENODEV; + } + mutex_lock(&gxp->power_mgr->pm_lock); + if (refcount_read(&(gxp->power_mgr->blk_wake_ref))) { + dev_err(gxp->dev, "%s: Wake lock not released\n", __func__); + mutex_unlock(&gxp->power_mgr->pm_lock); + return -EBUSY; + } + + ret = gxp_pm_blkpwr_down(gxp); + if (!ret) + gxp->power_mgr->curr_state = AUR_OFF; + mutex_unlock(&gxp->power_mgr->pm_lock); + return ret; +} + +int gxp_pm_get_blk_state(struct gxp_dev *gxp) +{ + int ret; + + if (!gxp->power_mgr) { + dev_err(gxp->dev, "%s: No PM found\n", __func__); + return -ENODEV; + } + mutex_lock(&gxp->power_mgr->pm_lock); + ret = gxp->power_mgr->curr_state; + mutex_unlock(&gxp->power_mgr->pm_lock); + + return ret; +} + +int gxp_pm_core_on(struct gxp_dev *gxp, uint core) +{ + int ret = 0; + + /* + * Check if TOP LPM is already on. + */ + WARN_ON(!gxp_lpm_is_initialized(gxp, LPM_TOP_PSM)); + + mutex_lock(&gxp->power_mgr->pm_lock); + ret = gxp_lpm_up(gxp, core); + if (ret) { + dev_err(gxp->dev, "%s: Core %d on fail\n", __func__, core); + mutex_unlock(&gxp->power_mgr->pm_lock); + return ret; + } + + gxp->power_mgr->pwr_state_req[core] = gxp->power_mgr->curr_state; + mutex_unlock(&gxp->power_mgr->pm_lock); + + dev_notice(gxp->dev, "%s: Core %d up\n", __func__, core); + return ret; +} + +int gxp_pm_core_off(struct gxp_dev *gxp, uint core) +{ + /* + * Check if TOP LPM is already on. + */ + WARN_ON(!gxp_lpm_is_initialized(gxp, LPM_TOP_PSM)); + + mutex_lock(&gxp->power_mgr->pm_lock); + gxp_lpm_down(gxp, core); + gxp->power_mgr->pwr_state_req[core] = AUR_OFF; + mutex_unlock(&gxp->power_mgr->pm_lock); + /* + * TODO: b/199467568 If all cores are off shutdown blk + */ + dev_notice(gxp->dev, "%s: Core %d down\n", __func__, core); + return 0; +} + +int gxp_pm_get_core_state(struct gxp_dev *gxp, uint core) +{ + int ret; + + mutex_lock(&gxp->power_mgr->pm_lock); + ret = gxp->power_mgr->pwr_state_req[core]; + mutex_unlock(&gxp->power_mgr->pm_lock); + + return ret; +} + +int gxp_pm_req_state(struct gxp_dev *gxp, uint core, enum aur_power_state state) +{ + int i; + unsigned long curr_max_state = AUR_OFF; + + if (core >= GXP_NUM_CORES) { + dev_err(gxp->dev, "Invalid core num %d\n", core); + return -EINVAL; + } + + if (state > AUR_MAX_ALLOW_STATE) { + dev_err(gxp->dev, "Invalid state %d\n", state); + return -EINVAL; + } + mutex_lock(&gxp->power_mgr->pm_lock); + gxp->power_mgr->pwr_state_req[core] = state; + for (i = 0; i < GXP_NUM_CORES; i++) { + if (gxp->power_mgr->pwr_state_req[i] >= curr_max_state) + curr_max_state = gxp->power_mgr->pwr_state_req[i]; + } + + if (state == AUR_OFF) + gxp_pm_core_off(gxp, core); + if (curr_max_state != gxp->power_mgr->curr_state && + curr_max_state > AUR_OFF) { + gxp_pm_blk_set_state_acpm(gxp, curr_max_state); + gxp->power_mgr->curr_state = curr_max_state; + } else { + /* + * TODO: b/199467568 If all cores are off shutdown blk + */ + } + mutex_unlock(&gxp->power_mgr->pm_lock); + + return 0; +} + +int gxp_pm_acquire_blk_wakelock(struct gxp_dev *gxp) +{ + mutex_lock(&gxp->power_mgr->pm_lock); + refcount_inc(&(gxp->power_mgr->blk_wake_ref)); + dev_dbg(gxp->dev, "Blk wakelock ref count: %d\n", + refcount_read(&(gxp->power_mgr->blk_wake_ref))); + mutex_unlock(&gxp->power_mgr->pm_lock); + return 0; +} + +int gxp_pm_release_blk_wakelock(struct gxp_dev *gxp) +{ + mutex_lock(&gxp->power_mgr->pm_lock); + if (refcount_read(&(gxp->power_mgr->blk_wake_ref))) { + refcount_dec(&(gxp->power_mgr->blk_wake_ref)); + } else { + dev_err(gxp->dev, "Blk wakelock is already zero\n"); + WARN_ON(1); + mutex_unlock(&gxp->power_mgr->pm_lock); + return -EIO; + } + mutex_unlock(&gxp->power_mgr->pm_lock); + dev_notice(gxp->dev, "Release blk wakelock\n"); + return 0; +} + +int gxp_pm_init(struct gxp_dev *gxp) +{ + struct gxp_power_manager *mgr; + int i; + + mgr = devm_kzalloc(gxp->dev, sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + mgr->gxp = gxp; + mutex_init(&mgr->pm_lock); + mgr->curr_state = AUR_OFF; + refcount_set(&(mgr->blk_wake_ref), 0); + for (i = 0; i < GXP_NUM_CORES; i++) + mgr->pwr_state_req[i] = AUR_OFF; + mgr->ops = &gxp_aur_ops; + gxp->power_mgr = mgr; + return 0; +} + +int gxp_pm_destroy(struct gxp_dev *gxp) +{ + struct gxp_power_manager *mgr; + + mgr = gxp->power_mgr; + mutex_destroy(&mgr->pm_lock); + return 0; +} |