|
@@ -25,6 +25,8 @@
|
|
|
#include <linux/clk-provider.h>
|
|
|
#include <linux/of_address.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
+#include <linux/reset-controller.h>
|
|
|
+#include <linux/slab.h>
|
|
|
#include <linux/init.h>
|
|
|
|
|
|
#include "clkc.h"
|
|
@@ -32,6 +34,13 @@
|
|
|
|
|
|
static DEFINE_SPINLOCK(clk_lock);
|
|
|
|
|
|
+static void __iomem *clk_base;
|
|
|
+
|
|
|
+struct meson8b_clk_reset {
|
|
|
+ struct reset_controller_dev reset;
|
|
|
+ void __iomem *base;
|
|
|
+};
|
|
|
+
|
|
|
static const struct pll_rate_table sys_pll_rate_table[] = {
|
|
|
PLL_RATE(312000000, 52, 1, 2),
|
|
|
PLL_RATE(336000000, 56, 1, 2),
|
|
@@ -691,20 +700,114 @@ static struct clk_divider *const meson8b_clk_dividers[] = {
|
|
|
&meson8b_mpeg_clk_div,
|
|
|
};
|
|
|
|
|
|
+static const struct meson8b_clk_reset_line {
|
|
|
+ u32 reg;
|
|
|
+ u8 bit_idx;
|
|
|
+} meson8b_clk_reset_bits[] = {
|
|
|
+ [CLKC_RESET_L2_CACHE_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 30
|
|
|
+ },
|
|
|
+ [CLKC_RESET_AXI_64_TO_128_BRIDGE_A5_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 29
|
|
|
+ },
|
|
|
+ [CLKC_RESET_SCU_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 28
|
|
|
+ },
|
|
|
+ [CLKC_RESET_CPU3_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 27
|
|
|
+ },
|
|
|
+ [CLKC_RESET_CPU2_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 26
|
|
|
+ },
|
|
|
+ [CLKC_RESET_CPU1_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 25
|
|
|
+ },
|
|
|
+ [CLKC_RESET_CPU0_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 24
|
|
|
+ },
|
|
|
+ [CLKC_RESET_A5_GLOBAL_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 18
|
|
|
+ },
|
|
|
+ [CLKC_RESET_A5_AXI_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 17
|
|
|
+ },
|
|
|
+ [CLKC_RESET_A5_ABP_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 16
|
|
|
+ },
|
|
|
+ [CLKC_RESET_AXI_64_TO_128_BRIDGE_MMC_SOFT_RESET] = {
|
|
|
+ .reg = HHI_SYS_CPU_CLK_CNTL1, .bit_idx = 30
|
|
|
+ },
|
|
|
+ [CLKC_RESET_VID_CLK_CNTL_SOFT_RESET] = {
|
|
|
+ .reg = HHI_VID_CLK_CNTL, .bit_idx = 15
|
|
|
+ },
|
|
|
+ [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_POST] = {
|
|
|
+ .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 7
|
|
|
+ },
|
|
|
+ [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_PRE] = {
|
|
|
+ .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 3
|
|
|
+ },
|
|
|
+ [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_POST] = {
|
|
|
+ .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 1
|
|
|
+ },
|
|
|
+ [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_PRE] = {
|
|
|
+ .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 0
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev,
|
|
|
+ unsigned long id, bool assert)
|
|
|
+{
|
|
|
+ struct meson8b_clk_reset *meson8b_clk_reset =
|
|
|
+ container_of(rcdev, struct meson8b_clk_reset, reset);
|
|
|
+ unsigned long flags;
|
|
|
+ const struct meson8b_clk_reset_line *reset;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (id >= ARRAY_SIZE(meson8b_clk_reset_bits))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ reset = &meson8b_clk_reset_bits[id];
|
|
|
+
|
|
|
+ spin_lock_irqsave(&clk_lock, flags);
|
|
|
+
|
|
|
+ val = readl(meson8b_clk_reset->base + reset->reg);
|
|
|
+ if (assert)
|
|
|
+ val |= BIT(reset->bit_idx);
|
|
|
+ else
|
|
|
+ val &= ~BIT(reset->bit_idx);
|
|
|
+ writel(val, meson8b_clk_reset->base + reset->reg);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&clk_lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int meson8b_clk_reset_assert(struct reset_controller_dev *rcdev,
|
|
|
+ unsigned long id)
|
|
|
+{
|
|
|
+ return meson8b_clk_reset_update(rcdev, id, true);
|
|
|
+}
|
|
|
+
|
|
|
+static int meson8b_clk_reset_deassert(struct reset_controller_dev *rcdev,
|
|
|
+ unsigned long id)
|
|
|
+{
|
|
|
+ return meson8b_clk_reset_update(rcdev, id, false);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct reset_control_ops meson8b_clk_reset_ops = {
|
|
|
+ .assert = meson8b_clk_reset_assert,
|
|
|
+ .deassert = meson8b_clk_reset_deassert,
|
|
|
+};
|
|
|
+
|
|
|
static int meson8b_clkc_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
- void __iomem *clk_base;
|
|
|
int ret, clkid, i;
|
|
|
struct clk_hw *parent_hw;
|
|
|
struct clk *parent_clk;
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
|
|
- /* Generic clocks and PLLs */
|
|
|
- clk_base = of_iomap(dev->of_node, 1);
|
|
|
- if (!clk_base) {
|
|
|
- pr_err("%s: Unable to map clk base\n", __func__);
|
|
|
+ if (!clk_base)
|
|
|
return -ENXIO;
|
|
|
- }
|
|
|
|
|
|
/* Populate base address for PLLs */
|
|
|
for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++)
|
|
@@ -744,7 +847,7 @@ static int meson8b_clkc_probe(struct platform_device *pdev)
|
|
|
/* FIXME convert to devm_clk_register */
|
|
|
ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[clkid]);
|
|
|
if (ret)
|
|
|
- goto iounmap;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -767,15 +870,11 @@ static int meson8b_clkc_probe(struct platform_device *pdev)
|
|
|
if (ret) {
|
|
|
pr_err("%s: failed to register clock notifier for cpu_clk\n",
|
|
|
__func__);
|
|
|
- goto iounmap;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
|
|
|
&meson8b_hw_onecell_data);
|
|
|
-
|
|
|
-iounmap:
|
|
|
- iounmap(clk_base);
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
static const struct of_device_id meson8b_clkc_match_table[] = {
|
|
@@ -794,3 +893,39 @@ static struct platform_driver meson8b_driver = {
|
|
|
};
|
|
|
|
|
|
builtin_platform_driver(meson8b_driver);
|
|
|
+
|
|
|
+static void __init meson8b_clkc_reset_init(struct device_node *np)
|
|
|
+{
|
|
|
+ struct meson8b_clk_reset *rstc;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Generic clocks, PLLs and some of the reset-bits */
|
|
|
+ clk_base = of_iomap(np, 1);
|
|
|
+ if (!clk_base) {
|
|
|
+ pr_err("%s: Unable to map clk base\n", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
|
|
|
+ if (!rstc)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Reset Controller */
|
|
|
+ rstc->base = clk_base;
|
|
|
+ rstc->reset.ops = &meson8b_clk_reset_ops;
|
|
|
+ rstc->reset.nr_resets = ARRAY_SIZE(meson8b_clk_reset_bits);
|
|
|
+ rstc->reset.of_node = np;
|
|
|
+ ret = reset_controller_register(&rstc->reset);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to register clkc reset controller: %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc",
|
|
|
+ meson8b_clkc_reset_init);
|
|
|
+CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc",
|
|
|
+ meson8b_clkc_reset_init);
|
|
|
+CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc",
|
|
|
+ meson8b_clkc_reset_init);
|