162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2012-2019 ARM Limited (or its affiliates). */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
762306a36Sopenharmony_ci#include "cc_driver.h"
862306a36Sopenharmony_ci#include "cc_buffer_mgr.h"
962306a36Sopenharmony_ci#include "cc_request_mgr.h"
1062306a36Sopenharmony_ci#include "cc_sram_mgr.h"
1162306a36Sopenharmony_ci#include "cc_hash.h"
1262306a36Sopenharmony_ci#include "cc_pm.h"
1362306a36Sopenharmony_ci#include "cc_fips.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define POWER_DOWN_ENABLE 0x01
1662306a36Sopenharmony_ci#define POWER_DOWN_DISABLE 0x00
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int cc_pm_suspend(struct device *dev)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct cc_drvdata *drvdata = dev_get_drvdata(dev);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	dev_dbg(dev, "set HOST_POWER_DOWN_EN\n");
2362306a36Sopenharmony_ci	fini_cc_regs(drvdata);
2462306a36Sopenharmony_ci	cc_iowrite(drvdata, CC_REG(HOST_POWER_DOWN_EN), POWER_DOWN_ENABLE);
2562306a36Sopenharmony_ci	clk_disable_unprepare(drvdata->clk);
2662306a36Sopenharmony_ci	return 0;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int cc_pm_resume(struct device *dev)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int rc;
3262306a36Sopenharmony_ci	struct cc_drvdata *drvdata = dev_get_drvdata(dev);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	dev_dbg(dev, "unset HOST_POWER_DOWN_EN\n");
3562306a36Sopenharmony_ci	/* Enables the device source clk */
3662306a36Sopenharmony_ci	rc = clk_prepare_enable(drvdata->clk);
3762306a36Sopenharmony_ci	if (rc) {
3862306a36Sopenharmony_ci		dev_err(dev, "failed getting clock back on. We're toast.\n");
3962306a36Sopenharmony_ci		return rc;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci	/* wait for Cryptocell reset completion */
4262306a36Sopenharmony_ci	if (!cc_wait_for_reset_completion(drvdata)) {
4362306a36Sopenharmony_ci		dev_err(dev, "Cryptocell reset not completed");
4462306a36Sopenharmony_ci		clk_disable_unprepare(drvdata->clk);
4562306a36Sopenharmony_ci		return -EBUSY;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	cc_iowrite(drvdata, CC_REG(HOST_POWER_DOWN_EN), POWER_DOWN_DISABLE);
4962306a36Sopenharmony_ci	rc = init_cc_regs(drvdata);
5062306a36Sopenharmony_ci	if (rc) {
5162306a36Sopenharmony_ci		dev_err(dev, "init_cc_regs (%x)\n", rc);
5262306a36Sopenharmony_ci		clk_disable_unprepare(drvdata->clk);
5362306a36Sopenharmony_ci		return rc;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	/* check if tee fips error occurred during power down */
5662306a36Sopenharmony_ci	cc_tee_handle_fips_error(drvdata);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	cc_init_hash_sram(drvdata);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciconst struct dev_pm_ops ccree_pm = {
6462306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(cc_pm_suspend, cc_pm_resume, NULL)
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciint cc_pm_get(struct device *dev)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	int rc = pm_runtime_get_sync(dev);
7062306a36Sopenharmony_ci	if (rc < 0) {
7162306a36Sopenharmony_ci		pm_runtime_put_noidle(dev);
7262306a36Sopenharmony_ci		return rc;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid cc_pm_put_suspend(struct device *dev)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
8162306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
8262306a36Sopenharmony_ci}
83