aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-05-14 23:24:53 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-05-14 23:29:48 +0530
commitc2817a6c5b728d9e4625206d68c44ca9d066abf5 (patch)
tree689118dc221b4f22bfedaac6dfdcde971815a313
parente199a6215ba2df3cebdf03f8a40ce76887f59042 (diff)
download96b-common-c2817a6c5b728d9e4625206d68c44ca9d066abf5.tar.gz
clk: Add common clock driver for BM1880 SoC
Add common clock driver for Bitmain BM1880 SoC. The clock controller on BM1880 has supplies clocks to all peripherals in the form of gate clocks and composite clocks (fixed factor + gate). Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r--drivers/clk/Kconfig6
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-bm1880.c235
3 files changed, 242 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e705aab9e38b..67a376d0dd8b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -296,6 +296,12 @@ config COMMON_CLK_FIXED_MMIO
help
Support for Memory Mapped IO Fixed clocks
+config COMMON_CLK_BM1880
+ bool "Clock driver for Bitmain BM1880 SoC"
+ depends on ARCH_BITMAIN || COMPILE_TEST
+ help
+ This driver supports the clocks on Bitmain BM1880 SoC.
+
source "drivers/clk/actions/Kconfig"
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 1db133652f0c..6e6d9e44aca6 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
obj-$(CONFIG_COMMON_CLK_BD718XX) += clk-bd718x7.o
+obj-$(CONFIG_COMMON_CLK_BM1880) += clk-bm1880.o
obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
diff --git a/drivers/clk/clk-bm1880.c b/drivers/clk/clk-bm1880.c
new file mode 100644
index 000000000000..f302362a5521
--- /dev/null
+++ b/drivers/clk/clk-bm1880.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bitmain BM1880 SoC clock driver
+ *
+ * Copyright (c) 2019 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/bm1880-clock.h>
+
+#define BM1880_REG_WIDTH 32
+
+static DEFINE_SPINLOCK(bm1880_clk_lock);
+
+struct bm1880_clock_data {
+ void __iomem *base;
+ struct clk_onecell_data clk_data;
+};
+
+struct bm1880_gate_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent;
+ unsigned long flags;
+ unsigned long gate_flags;
+};
+
+struct bm1880_composite_clock {
+ unsigned int id;
+ const char *name;
+ const char **parents;
+ unsigned int num_parents;
+ unsigned long rate;
+ unsigned long flags;
+ unsigned long gate_flags;
+ unsigned long rate_flags;
+};
+
+static const struct bm1880_gate_clock bm1880_gate_clks[] = {
+ { BM1880_CLK_A53, "clk_a53", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AHB_ROM, "clk_ahb_rom", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI_SRAM, "clk_axi_sram", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_DDR_AXI, "clk_ddr_axi", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_EFUSE, "clk_efuse", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_APB_EFUSE, "clk_apb_efuse", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI_EMMC, "clk_axi_emmc", NULL, 0, 0 },
+ { BM1880_CLK_EMMC, "clk_emmc", NULL, 0, 0 },
+ { BM1880_CLK_AXI_SD, "clk_axi_sd", NULL, 0, 0 },
+ { BM1880_CLK_SD, "clk_sd", NULL, 0, 0 },
+ { BM1880_CLK_AXI_ETH0, "clk_axi_eth0", NULL, 0, 0 },
+ { BM1880_CLK_AXI_ETH1, "clk_axi_eth1", NULL, 0, 0 },
+ { BM1880_CLK_AXI_GDMA, "clk_axi_gdma", NULL, 0, 0 },
+ { BM1880_CLK_APB_GPIO, "clk_apb_gpio", NULL, 0, 0 },
+ { BM1880_CLK_APB_GPIO_INTR, "clk_apb_gpio_intr", NULL, 0, 0 },
+ { BM1880_CLK_GPIO_DB, "clk_gpio_db", NULL, 0, 0 },
+ { BM1880_CLK_AXI_MINER, "clk_axi_miner", NULL, 0, 0 },
+ { BM1880_CLK_AHB_SF, "clk_ahb_sf", NULL, 0, 0 },
+ { BM1880_CLK_SDMA_AXI, "clk_sdma_axi", NULL, 0, 0 },
+ { BM1880_CLK_SDMA_AUD, "clk_sdma_aud", NULL, 0, 0 },
+ { BM1880_CLK_APB_I2C, "clk_apb_i2c", NULL, 0, 0 },
+ { BM1880_CLK_APB_WDT, "clk_apb_wdt", NULL, 0, 0 },
+ { BM1880_CLK_APB_JPEG, "clk_apb_jpeg", NULL, 0, 0 },
+ { BM1880_CLK_JPEG_AXI, "clk_jpeg_axi", NULL, 0, 0 },
+ { BM1880_CLK_AXI_NF, "clk_axi_nf", NULL, 0, 0 },
+ { BM1880_CLK_APB_NF, "clk_apb_nf", NULL, 0, 0 },
+ { BM1880_CLK_NF, "clk_nf", NULL, 0, 0 },
+ { BM1880_CLK_APB_PWM, "clk_apb_pwm", NULL, 0, 0 },
+ { BM1880_CLK_RV, "clk_rv", NULL, 0, 0 },
+ { BM1880_CLK_APB_SPI, "clk_apb_spi", NULL, 0, 0 },
+ { BM1880_CLK_TPU_AXI, "clk_tpu_axi", NULL, 0, 0 },
+ { BM1880_CLK_APB_UART, "clk_apb_uart", NULL, 0, 0 },
+ { BM1880_CLK_APB_I2S, "clk_apb_i2s", NULL, 0, 0 },
+ { BM1880_CLK_AXI_USB, "clk_axi_usb", NULL, 0, 0 },
+ { BM1880_CLK_APB_USB, "clk_apb_usb", NULL, 0, 0 },
+ { BM1880_CLK_APB_VIDEO, "clk_apb_video", NULL, 0, 0 },
+ { BM1880_CLK_VIDEO_AXI, "clk_video_axi", NULL, 0, 0 },
+ { BM1880_CLK_VPP_AXI, "clk_vpp_axi", NULL, 0, 0 },
+ { BM1880_CLK_APB_VPP, "clk_apb_vpp", NULL, 0, 0 },
+ { BM1880_CLK_AXI1, "clk_axi1", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI2, "clk_axi2", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI3, "clk_axi3", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI4, "clk_axi4", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI5, "clk_axi5", NULL, CLK_IGNORE_UNUSED, 0 },
+ { BM1880_CLK_AXI6, "clk_axi6", NULL, CLK_IGNORE_UNUSED, 0 },
+};
+
+static const struct bm1880_composite_clock bm1880_composite_clks[] = {
+ { BM1880_CLK_50M_A53, "clk_50m_a53", NULL, 0, 50000000, CLK_IGNORE_UNUSED, 0, 0 },
+ { BM1880_CLK_100K_EMMC, "clk_100k_emmc", NULL, 0, 100000, 0, 0, 0 },
+ { BM1880_CLK_100K_SD, "clk_100k_sd", NULL, 0, 100000, 0, 0, 0 },
+ { BM1880_CLK_500M_ETH0, "clk_500m_eth0", NULL, 0, 500000000, 0, 0, 0 },
+ { BM1880_CLK_500M_ETH1, "clk_500m_eth1", NULL, 0, 500000000, 0, 0, 0 },
+ { BM1880_CLK_UART_500M, "clk_uart_500m", NULL, 0, 500000000, 0, 0, 0 },
+ { BM1880_CLK_125M_USB, "clk_125m_usb", NULL, 0, 125000000, 0, 0, 0 },
+ { BM1880_CLK_33K_USB, "clk_33k_usb", NULL, 0, 33000, 0, 0, 0 },
+ { BM1880_CLK_12M_USB, "clk_12m_usb", NULL, 0, 12000000, 0, 0, 0 },
+};
+
+int bm1880_clk_register_gate(const struct bm1880_gate_clock *clks,
+ int num_clks, struct bm1880_clock_data *data)
+{
+ struct clk *clk;
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < num_clks; i++) {
+ clk = clk_register_gate(NULL, clks[i].name,
+ clks[i].parent,
+ clks[i].flags,
+ (base + ((clks[i].id / BM1880_REG_WIDTH) * 4)),
+ clks[i].id % BM1880_REG_WIDTH,
+ clks[i].gate_flags,
+ &bm1880_clk_lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ goto err_clk;
+ }
+
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+
+err_clk:
+ while (i--)
+ clk_unregister_gate(data->clk_data.clks[clks[i].id]);
+
+ return PTR_ERR(clk);
+}
+
+int bm1880_clk_register_composite(const struct bm1880_composite_clock *clks,
+ int num_clks, struct bm1880_clock_data *data)
+{
+ struct clk *clk;
+ struct clk_gate *gate = NULL;
+ struct clk_fixed_rate *rate = NULL;
+ void __iomem *base = data->base;
+ int i;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return -ENOMEM;
+
+ rate = kzalloc(sizeof(*rate), GFP_KERNEL);
+ if (!rate)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clks; i++) {
+ gate->flags = clks[i].gate_flags;
+ gate->reg = (base + ((clks[i].id / BM1880_REG_WIDTH) * 4));
+ gate->bit_idx = clks[i].id % BM1880_REG_WIDTH;
+ gate->lock = &bm1880_clk_lock;
+
+ rate->fixed_rate = clks[i].rate;
+ rate->flags = clks[i].rate_flags;
+
+ clk = clk_register_composite(NULL, clks[i].name,
+ clks[i].parents,
+ clks[i].num_parents,
+ NULL, NULL,
+ &rate->hw, &clk_fixed_rate_ops,
+ &gate->hw, &clk_gate_ops,
+ clks[i].flags);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ goto err_clk;
+ }
+
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+
+err_clk:
+ while (i--)
+ clk_unregister_composite(data->clk_data.clks[clks[i].id]);
+
+ return PTR_ERR(clk);
+}
+
+static void bm1880_clk_init(struct device_node *np)
+{
+ struct bm1880_clock_data *clk_data;
+ struct clk **clk_table;
+ void __iomem *base;
+ int num_clks;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%pOFn: unable to map resource", np);
+ of_node_put(np);
+ return;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->base = base;
+ num_clks = ARRAY_SIZE(bm1880_gate_clks) +
+ ARRAY_SIZE(bm1880_composite_clks);
+
+ clk_table = kcalloc(num_clks, sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table)
+ goto err_out;
+
+ clk_data->clk_data.clks = clk_table;
+ clk_data->clk_data.clk_num = num_clks;
+
+ /* Register Gate clocks */
+ bm1880_clk_register_gate(bm1880_gate_clks,
+ ARRAY_SIZE(bm1880_gate_clks),
+ clk_data);
+
+ /* Register Composite clocks */
+ bm1880_clk_register_composite(bm1880_composite_clks,
+ ARRAY_SIZE(bm1880_composite_clks),
+ clk_data);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
+
+ return;
+err_out:
+ kfree(clk_data);
+}
+
+CLK_OF_DECLARE_DRIVER(bm1880_clk, "bitmain,bm1880-clk", bm1880_clk_init);