unplugged-kernel/drivers/misc/mediatek/adsp/mt6779/adsp_clk.c

150 lines
3.6 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
//
// adsp_clk.c-- Mediatek ADSP clock control
//
// Copyright (c) 2018 MediaTek Inc.
// Author: Celine Liu <Celine.liu@mediatek.com>
#include <linux/clk.h>
#include "adsp_clk.h"
#include "adsp_helper.h"
struct adsp_clock_attr {
const char *name;
bool clk_prepare;
bool clk_status;
struct clk *clock;
};
static struct adsp_clock_attr adsp_clks[ADSP_CLK_NUM] = {
[CLK_ADSP_INFRA] = {"clk_adsp_infra", false, false, NULL},
[CLK_TOP_ADSP_SEL] = {"clk_top_adsp_sel", false, false, NULL},
[CLK_ADSP_CLK26M] = {"clk_adsp_clk26m", false, false, NULL},
[CLK_TOP_MMPLL_D4] = {"clk_top_mmpll_d4", false, false, NULL},
[CLK_TOP_ADSPPLL_D4] = {"clk_top_adsppll_d4", false, false, NULL},
[CLK_TOP_ADSPPLL_D6] = {"clk_top_adsppll_d6", false, false, NULL},
};
static uint32_t adsp_clock_count;
DEFINE_SPINLOCK(adsp_clock_spinlock);
int adsp_set_top_mux(enum adsp_clk clk)
{
int ret = 0;
struct clk *parent;
pr_debug("%s(%x)\n", __func__, clk);
switch (clk) {
case CLK_ADSP_CLK26M:
parent = adsp_clks[CLK_ADSP_CLK26M].clock;
break;
case CLK_TOP_MMPLL_D4:
parent = adsp_clks[CLK_TOP_MMPLL_D4].clock;
break;
case CLK_TOP_ADSPPLL_D4:
parent = adsp_clks[CLK_TOP_ADSPPLL_D4].clock;
break;
case CLK_TOP_ADSPPLL_D6:
parent = adsp_clks[CLK_TOP_ADSPPLL_D6].clock;
break;
default:
parent = adsp_clks[CLK_ADSP_CLK26M].clock;
break;
}
ret = clk_set_parent(adsp_clks[CLK_TOP_ADSP_SEL].clock, parent);
if (IS_ERR(&ret))
pr_err("[ADSP] %s clk_set_parent(clk_top_adsp_sel-%x) fail %d\n",
__func__, clk, ret);
return ret;
}
/* clock init */
int adsp_clk_device_probe(void *dev)
{
size_t i;
int ret = 0;
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);
} else {
adsp_clks[i].clk_status = true;
}
}
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(adsp_clks); i++) {
if (adsp_clks[i].clk_status) {
ret = clk_prepare(adsp_clks[i].clock);
if (ret) {
pr_err("%s clk_prepare %s fail %d\n", __func__,
adsp_clks[i].name, ret);
} else {
adsp_clks[i].clk_prepare = true;
}
}
}
return ret;
}
/* clock deinit */
void adsp_clk_device_remove(void *dev)
{
size_t i;
pr_debug("%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(adsp_clks); i++) {
if (adsp_clks[i].clock && !IS_ERR(adsp_clks[i].clock) &&
adsp_clks[i].clk_prepare) {
clk_unprepare(adsp_clks[i].clock);
adsp_clks[i].clk_prepare = false;
}
}
}
int adsp_enable_clock(void)
{
int ret = 0;
unsigned long spin_flags;
if (adsp_clks[CLK_ADSP_INFRA].clk_prepare) {
spin_lock_irqsave(&adsp_clock_spinlock, spin_flags);
if (++adsp_clock_count == 1)
/* unable to access adsp sram before set way_en to 1 */
adsp_way_en_ctrl(1);
ret = clk_enable(adsp_clks[CLK_ADSP_INFRA].clock);
if (IS_ERR(&ret))
pr_err("%s(), clk_enable %s fail, ret %d\n", __func__,
adsp_clks[CLK_ADSP_INFRA].name, ret);
spin_unlock_irqrestore(&adsp_clock_spinlock, spin_flags);
} else {
pr_err("%s(), clk %s, not prepared\n",
__func__, adsp_clks[CLK_ADSP_INFRA].name);
ret = -EINVAL;
}
return ret;
}
void adsp_disable_clock(void)
{
unsigned long spin_flags;
if (adsp_clks[CLK_ADSP_INFRA].clk_prepare) {
spin_lock_irqsave(&adsp_clock_spinlock, spin_flags);
if (--adsp_clock_count == 0)
/* unable to access adsp sram before set way_en to 1 */
adsp_way_en_ctrl(0);
clk_disable(adsp_clks[CLK_ADSP_INFRA].clock);
spin_unlock_irqrestore(&adsp_clock_spinlock, spin_flags);
}
}