136 lines
3 KiB
C
136 lines
3 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include "adsp_clk.h"
|
||
|
#include <linux/clk.h>
|
||
|
|
||
|
|
||
|
static uint32_t adsp_clock_count;
|
||
|
|
||
|
struct adsp_clock_attr {
|
||
|
const char *name;
|
||
|
struct clk *clock;
|
||
|
};
|
||
|
|
||
|
static struct adsp_clock_attr adsp_clks[ADSP_CLK_NUM] = {
|
||
|
[CLK_SCP_SYS_ADSP] = {"scp_sys_adsp", NULL},
|
||
|
[CLK_ADSP_CK_CG] = {"clk_adsp_ck_cg", NULL},
|
||
|
[CLK_TOP_ADSP_SEL] = {"clk_top_adsp_sel", NULL},
|
||
|
[CLK_TOP_CLK26M] = {"clk_top_clk26m", NULL},
|
||
|
[CLK_TOP_ADSPPLL] = {"clk_top_adsppll", NULL},
|
||
|
};
|
||
|
|
||
|
static struct adsp_clock_attr scp_clks[SCP_CLK_NUM] = {
|
||
|
[CLK_TOP_SCP_SEL] = {"clk_top_scp_sel", NULL},
|
||
|
};
|
||
|
|
||
|
int adsp_set_top_mux(enum adsp_clk clk)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_debug("%s(%x)\n", __func__, clk);
|
||
|
|
||
|
if (clk >= ADSP_CLK_NUM || clk < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = clk_set_parent(adsp_clks[CLK_TOP_ADSP_SEL].clock,
|
||
|
adsp_clks[clk].clock);
|
||
|
if (IS_ERR(&ret))
|
||
|
pr_err("[ADSP] %s clk_set_parent(clk_top_adsp_sel-%x) fail %d\n",
|
||
|
__func__, clk, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void adsp_set_clock_freq(enum adsp_clk clk)
|
||
|
{
|
||
|
switch (clk) {
|
||
|
case CLK_TOP_CLK26M:
|
||
|
case CLK_TOP_ADSPPLL:
|
||
|
adsp_set_top_mux(clk);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* clock init */
|
||
|
int adsp_clk_device_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
size_t i;
|
||
|
struct device *dev = &pdev->dev;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(adsp_clks); i++) {
|
||
|
adsp_clks[i].clock = devm_clk_get(dev, adsp_clks[i].name);
|
||
|
if (IS_ERR(adsp_clks[i].clock)) {
|
||
|
ret = PTR_ERR(adsp_clks[i].clock);
|
||
|
pr_err("%s devm_clk_get %s fail %d\n", __func__,
|
||
|
adsp_clks[i].name, ret);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(scp_clks); i++) {
|
||
|
scp_clks[i].clock = devm_clk_get(dev, scp_clks[i].name);
|
||
|
if (IS_ERR(scp_clks[i].clock)) {
|
||
|
ret = PTR_ERR(scp_clks[i].clock);
|
||
|
pr_info("%s devm_clk_get %s fail %d\n", __func__,
|
||
|
scp_clks[i].name, ret);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* clock deinit */
|
||
|
void adsp_clk_device_remove(void *dev)
|
||
|
{
|
||
|
pr_info("%s\n", __func__);
|
||
|
}
|
||
|
|
||
|
int adsp_enable_clock(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_debug("%s()\n", __func__);
|
||
|
|
||
|
/* enable scp clock before access adsp clock cg */
|
||
|
ret = clk_prepare_enable(scp_clks[CLK_TOP_SCP_SEL].clock);
|
||
|
if (IS_ERR(&ret)) {
|
||
|
pr_info("%s(), clk_prepare_enable %s fail, ret %d\n",
|
||
|
__func__, scp_clks[CLK_TOP_SCP_SEL].name, ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = clk_prepare_enable(adsp_clks[CLK_SCP_SYS_ADSP].clock);
|
||
|
if (IS_ERR(&ret)) {
|
||
|
pr_info("%s(), clk_prepare_enable %s fail, ret %d\n",
|
||
|
__func__, adsp_clks[CLK_SCP_SYS_ADSP].name, ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = clk_prepare_enable(adsp_clks[CLK_ADSP_CK_CG].clock);
|
||
|
if (IS_ERR(&ret)) {
|
||
|
pr_err("%s(), clk_prepare_enable %s fail, ret %d\n",
|
||
|
__func__, adsp_clks[CLK_ADSP_CK_CG].name, ret);
|
||
|
clk_disable_unprepare(adsp_clks[CLK_SCP_SYS_ADSP].clock);
|
||
|
return -EINVAL;
|
||
|
|
||
|
}
|
||
|
adsp_clock_count++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void adsp_disable_clock(void)
|
||
|
{
|
||
|
pr_debug("%s()\n", __func__);
|
||
|
|
||
|
adsp_clock_count--;
|
||
|
clk_disable_unprepare(adsp_clks[CLK_ADSP_CK_CG].clock);
|
||
|
clk_disable_unprepare(adsp_clks[CLK_SCP_SYS_ADSP].clock);
|
||
|
clk_disable_unprepare(scp_clks[CLK_TOP_SCP_SEL].clock);
|
||
|
|
||
|
}
|