blob: b40e407b0b42bdc33818e4ec0b6d91f28f1c59da [file] [log] [blame]
/*
* Copyright (C) 2018 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <mali_kbase.h>
#include "mali_kbase_config_platform.h"
#include <mali_kbase_defs.h>
static struct platform_device *probe_gpu_core1_dev;
static struct platform_device *probe_gpu_core2_dev;
static const struct of_device_id mtk_gpu_corex_of_ids[] = {
{ .compatible = "mediatek,gpu_core1", .data = "1" },
{ .compatible = "mediatek,gpu_core2", .data = "2" },
{}
};
static int mtk_gpu_corex_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const char *tmp;
match = of_match_device(mtk_gpu_corex_of_ids, dev);
if (!match)
return -ENODEV;
tmp = match->data;
if (*tmp == '1')
probe_gpu_core1_dev = pdev;
else
probe_gpu_core2_dev = pdev;
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
}
static int mtk_gpu_corex_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static struct platform_driver mtk_gpu_corex_driver = {
.probe = mtk_gpu_corex_probe,
.remove = mtk_gpu_corex_remove,
.driver = {
.name = "gpu_corex",
.of_match_table = mtk_gpu_corex_of_ids,
}
};
static int pm_callback_power_on(struct kbase_device *kbdev)
{
int error;
struct mfg_base *mfg = kbdev->platform_context;
if (mfg->is_powered) {
dev_dbg(kbdev->dev, "mali_device is already powered\n");
return 0;
}
error = pm_runtime_get_sync(kbdev->dev);
if (error < 0) {
dev_err(kbdev->dev,
"Power on core 0 failed (err: %d)\n", error);
return error;
}
error = pm_runtime_get_sync(&mfg->gpu_core1_dev->dev);
if (error < 0) {
dev_err(kbdev->dev,
"Power on core 1 failed (err: %d)\n", error);
return error;
}
error = pm_runtime_get_sync(&mfg->gpu_core2_dev->dev);
if (error < 0) {
dev_err(kbdev->dev,
"Power on core 2 failed (err: %d)\n", error);
return error;
}
error = clk_enable(mfg->clk_main_parent);
if (error < 0) {
dev_err(kbdev->dev,
"clk_main_parent clock enable failed (err: %d)\n",
error);
return error;
}
error = clk_enable(mfg->clk_mux);
if (error < 0) {
dev_err(kbdev->dev,
"clk_mux clock enable failed (err: %d)\n", error);
return error;
}
error = clk_enable(mfg->subsys_mfg_cg);
if (error < 0) {
dev_err(kbdev->dev,
"subsys_mfg_cg clock enable failed (err: %d)\n", error);
return error;
}
mfg->is_powered = true;
return 1;
}
static void pm_callback_power_off(struct kbase_device *kbdev)
{
struct mfg_base *mfg = kbdev->platform_context;
int error;
if (!mfg->is_powered) {
dev_dbg(kbdev->dev, "mali_device is already powered off\n");
return;
}
mfg->is_powered = false;
clk_disable(mfg->subsys_mfg_cg);
clk_disable(mfg->clk_mux);
clk_disable(mfg->clk_main_parent);
pm_runtime_mark_last_busy(&mfg->gpu_core2_dev->dev);
error = pm_runtime_put_autosuspend(&mfg->gpu_core2_dev->dev);
if (error < 0)
dev_err(kbdev->dev,
"Power off core 2 failed (err: %d)\n", error);
pm_runtime_mark_last_busy(&mfg->gpu_core1_dev->dev);
error = pm_runtime_put_autosuspend(&mfg->gpu_core1_dev->dev);
if (error < 0)
dev_err(kbdev->dev,
"Power off core 1 failed (err: %d)\n", error);
pm_runtime_mark_last_busy(kbdev->dev);
error = pm_runtime_put_autosuspend(kbdev->dev);
if (error < 0)
dev_err(kbdev->dev,
"Power off core 0 failed (err: %d)\n", error);
}
static int kbase_device_runtime_init(struct kbase_device *kbdev)
{
dev_dbg(kbdev->dev, "%s\n", __func__);
return 0;
}
static void kbase_device_runtime_disable(struct kbase_device *kbdev)
{
dev_dbg(kbdev->dev, "%s\n", __func__);
}
static int pm_callback_runtime_on(struct kbase_device *kbdev)
{
struct mfg_base *mfg = kbdev->platform_context;
int error, i;
for (i = 0; i < kbdev->nr_regulators; i++) {
error = regulator_enable(kbdev->regulators[i]);
if (error < 0) {
dev_err(kbdev->dev,
"Power on reg %d failed error = %d\n",
i, error);
return error;
}
}
error = clk_prepare(mfg->clk_main_parent);
if (error < 0) {
dev_err(kbdev->dev,
"clk_main_parent clock prepare failed (err: %d)\n",
error);
return error;
}
error = clk_prepare(mfg->clk_mux);
if (error < 0) {
dev_err(kbdev->dev,
"clk_mux clock prepare failed (err: %d)\n", error);
return error;
}
error = clk_prepare(mfg->subsys_mfg_cg);
if (error < 0) {
dev_err(kbdev->dev,
"subsys_mfg_cg clock prepare failed (err: %d)\n",
error);
return error;
}
return 0;
}
static void pm_callback_runtime_off(struct kbase_device *kbdev)
{
struct mfg_base *mfg = kbdev->platform_context;
int error, i;
clk_unprepare(mfg->subsys_mfg_cg);
clk_unprepare(mfg->clk_mux);
clk_unprepare(mfg->clk_main_parent);
for (i = 0; i < kbdev->nr_regulators; i++) {
error = regulator_disable(kbdev->regulators[i]);
if (error < 0) {
dev_err(kbdev->dev,
"Power off reg %d failed error = %d\n",
i, error);
}
}
}
static void pm_callback_resume(struct kbase_device *kbdev)
{
pm_callback_power_on(kbdev);
}
static void pm_callback_suspend(struct kbase_device *kbdev)
{
pm_callback_power_off(kbdev);
}
struct kbase_pm_callback_conf pm_callbacks = {
.power_on_callback = pm_callback_power_on,
.power_off_callback = pm_callback_power_off,
.power_suspend_callback = pm_callback_suspend,
.power_resume_callback = pm_callback_resume,
#ifdef KBASE_PM_RUNTIME
.power_runtime_init_callback = kbase_device_runtime_init,
.power_runtime_term_callback = kbase_device_runtime_disable,
.power_runtime_on_callback = pm_callback_runtime_on,
.power_runtime_off_callback = pm_callback_runtime_off,
#else /* KBASE_PM_RUNTIME */
.power_runtime_init_callback = NULL,
.power_runtime_term_callback = NULL,
.power_runtime_on_callback = NULL,
.power_runtime_off_callback = NULL,
#endif /* KBASE_PM_RUNTIME */
};
int mali_mfgsys_init(struct kbase_device *kbdev, struct mfg_base *mfg)
{
int err = 0, i;
unsigned long volt;
if (!probe_gpu_core1_dev || !probe_gpu_core2_dev)
return -EPROBE_DEFER;
for (i = 0; i < kbdev->nr_regulators; i++)
if (kbdev->regulators[i] == NULL)
return -EINVAL;
mfg->gpu_core1_dev = probe_gpu_core1_dev;
mfg->gpu_core2_dev = probe_gpu_core2_dev;
mfg->clk_main_parent = devm_clk_get(kbdev->dev, "clk_main_parent");
if (IS_ERR(mfg->clk_main_parent)) {
err = PTR_ERR(mfg->clk_main_parent);
dev_err(kbdev->dev, "devm_clk_get clk_main_parent failed\n");
return err;
}
mfg->clk_sub_parent = devm_clk_get(kbdev->dev, "clk_sub_parent");
if (IS_ERR(mfg->clk_sub_parent)) {
err = PTR_ERR(mfg->clk_sub_parent);
dev_err(kbdev->dev, "devm_clk_get clk_sub_parent failed\n");
return err;
}
mfg->clk_mux = devm_clk_get(kbdev->dev, "clk_mux");
if (IS_ERR(mfg->clk_mux)) {
err = PTR_ERR(mfg->clk_mux);
dev_err(kbdev->dev, "devm_clk_get clk_mux failed\n");
return err;
}
mfg->subsys_mfg_cg = devm_clk_get(kbdev->dev, "subsys_mfg_cg");
if (IS_ERR(mfg->subsys_mfg_cg)) {
err = PTR_ERR(mfg->subsys_mfg_cg);
dev_err(kbdev->dev, "devm_clk_get subsys_mfg_cg failed\n");
return err;
}
for (i = 0; i < kbdev->nr_regulators; i++) {
volt = (0 == i) ? VGPU_MAX_VOLT : VSRAM_GPU_MAX_VOLT;
err = regulator_set_voltage(kbdev->regulators[i],
volt, volt + VOLT_TOL);
if (err < 0) {
dev_err(kbdev->dev,
"Regulator %d set voltage failed: %d\n",
i, err);
return err;
}
kbdev->current_voltages[i] = volt;
}
mfg->is_powered = false;
return 0;
}
static int platform_init(struct kbase_device *kbdev)
{
int err;
struct mfg_base *mfg;
mfg = kzalloc(sizeof(*mfg), GFP_KERNEL);
if (!mfg)
return -ENOMEM;
err = mali_mfgsys_init(kbdev, mfg);
if (err)
goto platform_init_err;
kbdev->platform_context = mfg;
pm_runtime_set_autosuspend_delay(kbdev->dev, 50);
pm_runtime_use_autosuspend(kbdev->dev);
pm_runtime_enable(kbdev->dev);
err = clk_set_parent(mfg->clk_mux, mfg->clk_sub_parent);
if (err) {
dev_err(kbdev->dev, "Failed to select sub clock src\n");
goto platform_init_err;
}
err = clk_set_rate(mfg->clk_main_parent, GPU_FREQ_KHZ_MAX * 1000);
if (err) {
dev_err(kbdev->dev, "Failed to set clock %d kHz\n",
GPU_FREQ_KHZ_MAX);
goto platform_init_err;
}
err = clk_set_parent(mfg->clk_mux, mfg->clk_main_parent);
if (err) {
dev_err(kbdev->dev, "Failed to select main clock src\n");
goto platform_init_err;
}
return 0;
platform_init_err:
kfree(mfg);
return err;
}
static void platform_term(struct kbase_device *kbdev)
{
struct mfg_base *mfg = kbdev->platform_context;
kfree(mfg);
kbdev->platform_context = NULL;
pm_runtime_disable(kbdev->dev);
}
struct kbase_platform_funcs_conf platform_funcs = {
.platform_init_func = platform_init,
.platform_term_func = platform_term
};
static int __init mtk_mfg_corex(void)
{
int ret;
ret = platform_driver_register(&mtk_gpu_corex_driver);
if (ret != 0)
pr_debug("%s: Failed to register GPU core driver", __func__);
return ret;
}
subsys_initcall(mtk_mfg_corex);