sdk-hwV1.3/lichee/linux-4.9/drivers/remoteproc/sunxi_boot/e907/e907.c

272 lines
6.2 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/clk.h>
#include <linux/clk/sunxi.h>
#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <asm/io.h>
#include "../sunxi_rproc_boot_internal.h"
/* hardware related operations for sun8iw21-e907 core */
struct sunxi_e907 {
struct device *dev;
struct platform_device *pdev;
struct clk *pclk; /* e907 pll clk */
struct clk *clk; /* e907 clk */
struct clk *gate; /* e907 clk gating */
struct clk *axi; /* e907 axi clk */
u32 freq;
struct clk *mbox; /* msgbox clk */
void __iomem *addr; /* e907 start addr register */
u32 boot_addr;
};
static int sunxi_e907_init(struct platform_device *pdev,
struct sunxi_core *core)
{
struct sunxi_e907 *info;
struct resource *res;
struct device *dev = &pdev->dev;
int ret;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (IS_ERR_OR_NULL(info))
return -ENOMEM;
core->priv = info;
info->dev = dev;
info->pdev = pdev;
info->pclk = devm_clk_get(info->dev, "pll");
if (IS_ERR(info->pclk)) {
dev_err(info->dev, "no find pll in dts, ret=%ld\n", PTR_ERR(info->pclk));
ret = PTR_ERR(info->pclk);
goto err_pll;
}
ret = clk_prepare(info->pclk);
if (ret) {
dev_err(info->dev, "pll clk prepare failed\n");
goto err_pll;
}
info->clk = devm_clk_get(info->dev, "clk");
if (IS_ERR(info->clk)) {
dev_err(info->dev, "no find clk in dts, ret=%ld\n", PTR_ERR(info->clk));
ret = PTR_ERR(info->clk);
goto err_clk;
}
ret = clk_prepare(info->clk);
if (ret) {
dev_err(info->dev, "clk prepare failed\n");
goto err_clk;
}
info->axi = devm_clk_get(info->dev, "axi");
if (IS_ERR(info->axi)) {
dev_err(info->dev, "no find axi clk in dts, ret=%ld\n", PTR_ERR(info->axi));
ret = PTR_ERR(info->axi);
goto err_axi;
}
ret = clk_prepare(info->axi);
if (ret) {
dev_err(info->dev, "axi clk prepare failed\n");
goto err_axi;
}
info->gate = devm_clk_get(info->dev, "gate");
if (IS_ERR(info->gate)) {
dev_err(info->dev, "no find gate in dts, ret=%ld\n", PTR_ERR(info->gate));
ret = PTR_ERR(info->gate);
goto err_gate;
}
ret = clk_prepare(info->gate);
if (ret) {
dev_err(info->dev, "gate prepare failed\n");
goto err_gate;
}
info->mbox = devm_clk_get(info->dev, "mbox");
if (IS_ERR(info->mbox)) {
dev_err(info->dev, "no find mbox clk in dts, ret=%ld\n", PTR_ERR(info->gate));
ret = PTR_ERR(info->mbox);
goto err_mbox;
}
ret = of_property_read_u32(info->dev->of_node, "clock-frequency", &info->freq);
if (ret) {
dev_err(info->dev, "fail to get clock-frequency");
ret = -ENXIO;
goto err_freq;
}
res = platform_get_resource_byname(info->pdev,
IORESOURCE_MEM, "riscv-addr");
if (!res) {
dev_err(info->dev, "no find riscv-addr reg in dts\n");
ret = -ENODEV;
goto err_addr;
}
info->addr = devm_ioremap_resource(info->dev, res);
if (IS_ERR(info->addr)) {
dev_err(info->dev, "ioreamp riscv-addr register "
"failed,ret=%ld\n", PTR_ERR(info->addr));
ret = PTR_ERR(info->addr);
goto err_ioremap;
}
return 0;
err_ioremap:
err_addr:
err_freq:
devm_clk_put(info->dev, info->mbox);
err_mbox:
devm_clk_put(info->dev, info->gate);
err_gate:
devm_clk_put(info->dev, info->axi);
err_axi:
devm_clk_put(info->dev, info->clk);
err_clk:
devm_clk_put(info->dev, info->pclk);
err_pll:
return ret;
}
static void sunxi_e907_deinit(struct sunxi_core *core)
{
struct sunxi_e907 *info = core->priv;
clk_unprepare(info->gate);
clk_unprepare(info->axi);
clk_unprepare(info->clk);
clk_unprepare(info->pclk);
clk_unprepare(info->axi);
devm_clk_put(info->dev, info->mbox);
devm_clk_put(info->dev, info->gate);
devm_clk_put(info->dev, info->axi);
devm_clk_put(info->dev, info->clk);
devm_clk_put(info->dev, info->pclk);
devm_iounmap(info->dev, info->addr);
}
static int sunxi_e907_start(struct sunxi_core *core)
{
struct sunxi_e907 *info = core->priv;
int rate = 0;
int ret;
if (__clk_get_enable_count(info->mbox))
clk_disable_unprepare(info->mbox);
ret = clk_enable(info->pclk);
if (ret < 0) {
dev_err(info->dev, "pll clk enable failed.\n");
return ret;
}
ret = clk_set_parent(info->clk, info->pclk);
if (ret < 0) {
dev_err(info->dev, "set clk parent to pll clk failed.\n");
return ret;
}
rate = clk_round_rate(info->clk, info->freq);
ret = clk_set_rate(info->clk, rate);
if (ret < 0) {
dev_err(info->dev, "set e907 clk rate to %d failed.\n", rate);
return ret;
}
ret = clk_enable(info->clk);
if (ret < 0) {
dev_err(info->dev, "e907 clk enable failed.\n");
return ret;
}
ret = clk_set_parent(info->axi, info->clk);
if (ret < 0) {
dev_err(info->dev, "set axi clk parent to clk failed.\n");
return ret;
}
rate = clk_round_rate(info->axi, info->freq / 2);
ret = clk_set_rate(info->axi, rate);
if (ret < 0) {
dev_err(info->dev, "set axi clk rate to %d failed.\n", rate);
return ret;
}
ret = clk_enable(info->axi);
if (ret < 0) {
dev_err(info->dev, "axi clk enable failed.\n");
return ret;
}
/* Set Cpu boot addr */
writel(info->boot_addr, info->addr);
/*
* In order to clear the msgbox data in the fifo,
* we need to reset the riscv msgbox
*/
clk_prepare_enable(info->mbox);
clk_enable(info->gate);
return 0;
}
static int sunxi_e907_is_start(struct sunxi_core *core)
{
struct sunxi_e907 *info = core->priv;
return __clk_is_enabled(info->gate);
}
static int sunxi_e907_stop(struct sunxi_core *core)
{
struct sunxi_e907 *info = core->priv;
if (__clk_get_enable_count(info->gate))
clk_disable(info->gate);
if (__clk_get_enable_count(info->clk))
clk_disable(info->clk);
return 0;
}
static void sunxi_e907_set_start_addr(struct sunxi_core *core, u32 addr)
{
struct sunxi_e907 *info = core->priv;
info->boot_addr = addr;
}
static struct sunxi_remote_core_ops core_ops_e907 = {
.init = sunxi_e907_init,
.deinit = sunxi_e907_deinit,
.start = sunxi_e907_start,
.is_start = sunxi_e907_is_start,
.stop = sunxi_e907_stop,
.set_start_addr = sunxi_e907_set_start_addr,
};
DEFINE_RPROC_CORE(e907, core_ops_e907, "allwinner,riscv-e907");