diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/clk/hisilicon | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | drivers/clk/hisilicon/Kconfig | 60 | ||||
-rw-r--r-- | drivers/clk/hisilicon/Makefile | 18 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi3519.c | 215 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi3620.c | 497 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi3660-stub.c | 187 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi3660.c | 641 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi6220-stub.c | 276 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi6220.c | 309 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hip04.c | 56 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hisi-phase.c | 121 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hix5hd2.c | 322 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk.c | 357 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk.h | 171 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clkdivider-hi6220.c | 158 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clkgate-separated.c | 127 | ||||
-rw-r--r-- | drivers/clk/hisilicon/crg-hi3516cv300.c | 330 | ||||
-rw-r--r-- | drivers/clk/hisilicon/crg-hi3798cv200.c | 413 | ||||
-rw-r--r-- | drivers/clk/hisilicon/crg.h | 34 | ||||
-rw-r--r-- | drivers/clk/hisilicon/reset.c | 132 | ||||
-rw-r--r-- | drivers/clk/hisilicon/reset.h | 37 |
20 files changed, 4461 insertions, 0 deletions
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000..becdb1dd2 --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,60 @@ +config COMMON_CLK_HI3516CV300 + tristate "HI3516CV300 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3516cv300. + +config COMMON_CLK_HI3519 + tristate "Hi3519 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3519. + +config COMMON_CLK_HI3660 + bool "Hi3660 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the clock driver for hi3660. + +config COMMON_CLK_HI3798CV200 + tristate "Hi3798CV200 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3798cv200. + +config COMMON_CLK_HI6220 + bool "Hi6220 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the Hisilicon Hi6220 clock driver based on the common clock framework. + +config RESET_HISI + bool "HiSilicon Reset Controller Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_CONTROLLER + help + Build reset controller driver for HiSilicon device chipsets. + +config STUB_CLK_HI6220 + bool "Hi6220 Stub Clock Driver" if EXPERT + depends on (COMMON_CLK_HI6220 || COMPILE_TEST) + depends on MAILBOX + default COMMON_CLK_HI6220 + help + Build the Hisilicon Hi6220 stub clock driver. + +config STUB_CLK_HI3660 + bool "Hi3660 Stub Clock Driver" if EXPERT + depends on (COMMON_CLK_HI3660 || COMPILE_TEST) + depends on MAILBOX + default COMMON_CLK_HI3660 + help + Build the Hisilicon Hi3660 stub clock driver. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile new file mode 100644 index 000000000..2a714c0f9 --- /dev/null +++ b/drivers/clk/hisilicon/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Hisilicon Clock specific Makefile +# + +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o clk-hisi-phase.o + +obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o +obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o +obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o +obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o +obj-$(CONFIG_COMMON_CLK_HI3660) += clk-hi3660.o +obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o +obj-$(CONFIG_RESET_HISI) += reset.o +obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o +obj-$(CONFIG_STUB_CLK_HI3660) += clk-hi3660-stub.o diff --git a/drivers/clk/hisilicon/clk-hi3519.c b/drivers/clk/hisilicon/clk-hi3519.c new file mode 100644 index 000000000..51b173ef1 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3519.c @@ -0,0 +1,215 @@ +/* + * Hi3519 Clock Driver + * + * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/hi3519-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "reset.h" + +#define HI3519_INNER_CLK_OFFSET 64 +#define HI3519_FIXED_24M 65 +#define HI3519_FIXED_50M 66 +#define HI3519_FIXED_75M 67 +#define HI3519_FIXED_125M 68 +#define HI3519_FIXED_150M 69 +#define HI3519_FIXED_200M 70 +#define HI3519_FIXED_250M 71 +#define HI3519_FIXED_300M 72 +#define HI3519_FIXED_400M 73 +#define HI3519_FMC_MUX 74 + +#define HI3519_NR_CLKS 128 + +struct hi3519_crg_data { + struct hisi_clock_data *clk_data; + struct hisi_reset_controller *rstc; +}; + +static const struct hisi_fixed_rate_clock hi3519_fixed_rate_clks[] = { + { HI3519_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3519_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3519_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HI3519_FIXED_125M, "125m", NULL, 0, 125000000, }, + { HI3519_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3519_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HI3519_FIXED_250M, "250m", NULL, 0, 250000000, }, + { HI3519_FIXED_300M, "300m", NULL, 0, 300000000, }, + { HI3519_FIXED_400M, "400m", NULL, 0, 400000000, }, +}; + +static const char *const fmc_mux_p[] = { + "24m", "75m", "125m", "150m", "200m", "250m", "300m", "400m", }; +static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; + +static const struct hisi_mux_clock hi3519_mux_clks[] = { + { HI3519_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), + CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, +}; + +static const struct hisi_gate_clock hi3519_gate_clks[] = { + { HI3519_FMC_CLK, "clk_fmc", "fmc_mux", + CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, + { HI3519_UART0_CLK, "clk_uart0", "24m", + CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, + { HI3519_UART1_CLK, "clk_uart1", "24m", + CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, + { HI3519_UART2_CLK, "clk_uart2", "24m", + CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, + { HI3519_UART3_CLK, "clk_uart3", "24m", + CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, + { HI3519_UART4_CLK, "clk_uart4", "24m", + CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, + { HI3519_SPI0_CLK, "clk_spi0", "50m", + CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, + { HI3519_SPI1_CLK, "clk_spi1", "50m", + CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, + { HI3519_SPI2_CLK, "clk_spi2", "50m", + CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, +}; + +static struct hisi_clock_data *hi3519_clk_register(struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3519_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3519_fixed_rate_clks, + ARRAY_SIZE(hi3519_fixed_rate_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3519_mux_clks, + ARRAY_SIZE(hi3519_mux_clks), + clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3519_gate_clks, + ARRAY_SIZE(hi3519_gate_clks), + clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3519_fixed_rate_clks, + ARRAY_SIZE(hi3519_fixed_rate_clks), + clk_data); + +unregister_mux: + hisi_clk_unregister_mux(hi3519_mux_clks, + ARRAY_SIZE(hi3519_mux_clks), + clk_data); +unregister_gate: + hisi_clk_unregister_gate(hi3519_gate_clks, + ARRAY_SIZE(hi3519_gate_clks), + clk_data); + return ERR_PTR(ret); +} + +static void hi3519_clk_unregister(struct platform_device *pdev) +{ + struct hi3519_crg_data *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3519_gate_clks, + ARRAY_SIZE(hi3519_mux_clks), + crg->clk_data); + hisi_clk_unregister_mux(hi3519_mux_clks, + ARRAY_SIZE(hi3519_mux_clks), + crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3519_fixed_rate_clks, + ARRAY_SIZE(hi3519_fixed_rate_clks), + crg->clk_data); +} + +static int hi3519_clk_probe(struct platform_device *pdev) +{ + struct hi3519_crg_data *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = hi3519_clk_register(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3519_clk_remove(struct platform_device *pdev) +{ + struct hi3519_crg_data *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + hi3519_clk_unregister(pdev); + return 0; +} + + +static const struct of_device_id hi3519_clk_match_table[] = { + { .compatible = "hisilicon,hi3519-crg" }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3519_clk_match_table); + +static struct platform_driver hi3519_clk_driver = { + .probe = hi3519_clk_probe, + .remove = hi3519_clk_remove, + .driver = { + .name = "hi3519-clk", + .of_match_table = hi3519_clk_match_table, + }, +}; + +static int __init hi3519_clk_init(void) +{ + return platform_driver_register(&hi3519_clk_driver); +} +core_initcall(hi3519_clk_init); + +static void __exit hi3519_clk_exit(void) +{ + platform_driver_unregister(&hi3519_clk_driver); +} +module_exit(hi3519_clk_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3519 Clock Driver"); diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c new file mode 100644 index 000000000..77072c777 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -0,0 +1,497 @@ +/* + * Hisilicon Hi3620 clock driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * Xin Li <li.xin@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/hi3620-clock.h> + +#include "clk.h" + +/* clock parent list */ +static const char *const timer0_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer1_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer2_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer3_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer4_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer5_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer6_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer7_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer8_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const timer9_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const uart0_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart1_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart2_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart3_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart4_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const spi0_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi1_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi2_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +/* share axi parent */ +static const char *const saxi_mux_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const pwm0_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const pwm1_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const sd_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux2_p[] __initconst = { "osc26m", "mmc1_div", }; +static const char *const g2d_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const venc_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vdec_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vpp_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const edc0_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi0_mux_p[] __initconst = { "armpll2", "armpll4", + "armpll3", "armpll5", }; +static const char *const edc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi1_mux_p[] __initconst = { "armpll2", "armpll4", + "armpll3", "armpll5", }; +static const char *const rclk_hsic_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const mmc2_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc3_mux_p[] __initconst = { "armpll2", "armpll3", }; + + +/* fixed rate clocks */ +static struct hisi_fixed_rate_clock hi3620_fixed_rate_clks[] __initdata = { + { HI3620_OSC32K, "osc32k", NULL, 0, 32768, }, + { HI3620_OSC26M, "osc26m", NULL, 0, 26000000, }, + { HI3620_PCLK, "pclk", NULL, 0, 26000000, }, + { HI3620_PLL_ARM0, "armpll0", NULL, 0, 1600000000, }, + { HI3620_PLL_ARM1, "armpll1", NULL, 0, 1600000000, }, + { HI3620_PLL_PERI, "armpll2", NULL, 0, 1440000000, }, + { HI3620_PLL_USB, "armpll3", NULL, 0, 1440000000, }, + { HI3620_PLL_HDMI, "armpll4", NULL, 0, 1188000000, }, + { HI3620_PLL_GPU, "armpll5", NULL, 0, 1300000000, }, +}; + +/* fixed factor clocks */ +static struct hisi_fixed_factor_clock hi3620_fixed_factor_clks[] __initdata = { + { HI3620_RCLK_TCXO, "rclk_tcxo", "osc26m", 1, 4, 0, }, + { HI3620_RCLK_CFGAXI, "rclk_cfgaxi", "armpll2", 1, 30, 0, }, + { HI3620_RCLK_PICO, "rclk_pico", "hsic_div", 1, 40, 0, }, +}; + +static struct hisi_mux_clock hi3620_mux_clks[] __initdata = { + { HI3620_TIMER0_MUX, "timer0_mux", timer0_mux_p, ARRAY_SIZE(timer0_mux_p), CLK_SET_RATE_PARENT, 0, 15, 2, 0, }, + { HI3620_TIMER1_MUX, "timer1_mux", timer1_mux_p, ARRAY_SIZE(timer1_mux_p), CLK_SET_RATE_PARENT, 0, 17, 2, 0, }, + { HI3620_TIMER2_MUX, "timer2_mux", timer2_mux_p, ARRAY_SIZE(timer2_mux_p), CLK_SET_RATE_PARENT, 0, 19, 2, 0, }, + { HI3620_TIMER3_MUX, "timer3_mux", timer3_mux_p, ARRAY_SIZE(timer3_mux_p), CLK_SET_RATE_PARENT, 0, 21, 2, 0, }, + { HI3620_TIMER4_MUX, "timer4_mux", timer4_mux_p, ARRAY_SIZE(timer4_mux_p), CLK_SET_RATE_PARENT, 0x18, 0, 2, 0, }, + { HI3620_TIMER5_MUX, "timer5_mux", timer5_mux_p, ARRAY_SIZE(timer5_mux_p), CLK_SET_RATE_PARENT, 0x18, 2, 2, 0, }, + { HI3620_TIMER6_MUX, "timer6_mux", timer6_mux_p, ARRAY_SIZE(timer6_mux_p), CLK_SET_RATE_PARENT, 0x18, 4, 2, 0, }, + { HI3620_TIMER7_MUX, "timer7_mux", timer7_mux_p, ARRAY_SIZE(timer7_mux_p), CLK_SET_RATE_PARENT, 0x18, 6, 2, 0, }, + { HI3620_TIMER8_MUX, "timer8_mux", timer8_mux_p, ARRAY_SIZE(timer8_mux_p), CLK_SET_RATE_PARENT, 0x18, 8, 2, 0, }, + { HI3620_TIMER9_MUX, "timer9_mux", timer9_mux_p, ARRAY_SIZE(timer9_mux_p), CLK_SET_RATE_PARENT, 0x18, 10, 2, 0, }, + { HI3620_UART0_MUX, "uart0_mux", uart0_mux_p, ARRAY_SIZE(uart0_mux_p), CLK_SET_RATE_PARENT, 0x100, 7, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_UART1_MUX, "uart1_mux", uart1_mux_p, ARRAY_SIZE(uart1_mux_p), CLK_SET_RATE_PARENT, 0x100, 8, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_UART2_MUX, "uart2_mux", uart2_mux_p, ARRAY_SIZE(uart2_mux_p), CLK_SET_RATE_PARENT, 0x100, 9, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_UART3_MUX, "uart3_mux", uart3_mux_p, ARRAY_SIZE(uart3_mux_p), CLK_SET_RATE_PARENT, 0x100, 10, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_UART4_MUX, "uart4_mux", uart4_mux_p, ARRAY_SIZE(uart4_mux_p), CLK_SET_RATE_PARENT, 0x100, 11, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_SPI0_MUX, "spi0_mux", spi0_mux_p, ARRAY_SIZE(spi0_mux_p), CLK_SET_RATE_PARENT, 0x100, 12, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_SPI1_MUX, "spi1_mux", spi1_mux_p, ARRAY_SIZE(spi1_mux_p), CLK_SET_RATE_PARENT, 0x100, 13, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_SPI2_MUX, "spi2_mux", spi2_mux_p, ARRAY_SIZE(spi2_mux_p), CLK_SET_RATE_PARENT, 0x100, 14, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_SAXI_MUX, "saxi_mux", saxi_mux_p, ARRAY_SIZE(saxi_mux_p), CLK_SET_RATE_PARENT, 0x100, 15, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_PWM0_MUX, "pwm0_mux", pwm0_mux_p, ARRAY_SIZE(pwm0_mux_p), CLK_SET_RATE_PARENT, 0x104, 10, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_PWM1_MUX, "pwm1_mux", pwm1_mux_p, ARRAY_SIZE(pwm1_mux_p), CLK_SET_RATE_PARENT, 0x104, 11, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_SD_MUX, "sd_mux", sd_mux_p, ARRAY_SIZE(sd_mux_p), CLK_SET_RATE_PARENT, 0x108, 4, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_MMC1_MUX, "mmc1_mux", mmc1_mux_p, ARRAY_SIZE(mmc1_mux_p), CLK_SET_RATE_PARENT, 0x108, 9, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_MMC1_MUX2, "mmc1_mux2", mmc1_mux2_p, ARRAY_SIZE(mmc1_mux2_p), CLK_SET_RATE_PARENT, 0x108, 10, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_G2D_MUX, "g2d_mux", g2d_mux_p, ARRAY_SIZE(g2d_mux_p), CLK_SET_RATE_PARENT, 0x10c, 5, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_VENC_MUX, "venc_mux", venc_mux_p, ARRAY_SIZE(venc_mux_p), CLK_SET_RATE_PARENT, 0x10c, 11, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_VDEC_MUX, "vdec_mux", vdec_mux_p, ARRAY_SIZE(vdec_mux_p), CLK_SET_RATE_PARENT, 0x110, 5, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_VPP_MUX, "vpp_mux", vpp_mux_p, ARRAY_SIZE(vpp_mux_p), CLK_SET_RATE_PARENT, 0x110, 11, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_EDC0_MUX, "edc0_mux", edc0_mux_p, ARRAY_SIZE(edc0_mux_p), CLK_SET_RATE_PARENT, 0x114, 6, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_LDI0_MUX, "ldi0_mux", ldi0_mux_p, ARRAY_SIZE(ldi0_mux_p), CLK_SET_RATE_PARENT, 0x114, 13, 2, CLK_MUX_HIWORD_MASK, }, + { HI3620_EDC1_MUX, "edc1_mux", edc1_mux_p, ARRAY_SIZE(edc1_mux_p), CLK_SET_RATE_PARENT, 0x118, 6, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_LDI1_MUX, "ldi1_mux", ldi1_mux_p, ARRAY_SIZE(ldi1_mux_p), CLK_SET_RATE_PARENT, 0x118, 14, 2, CLK_MUX_HIWORD_MASK, }, + { HI3620_RCLK_HSIC, "rclk_hsic", rclk_hsic_p, ARRAY_SIZE(rclk_hsic_p), CLK_SET_RATE_PARENT, 0x130, 2, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), CLK_SET_RATE_PARENT, 0x140, 4, 1, CLK_MUX_HIWORD_MASK, }, + { HI3620_MMC3_MUX, "mmc3_mux", mmc3_mux_p, ARRAY_SIZE(mmc3_mux_p), CLK_SET_RATE_PARENT, 0x140, 9, 1, CLK_MUX_HIWORD_MASK, }, +}; + +static struct hisi_divider_clock hi3620_div_clks[] __initdata = { + { HI3620_SHAREAXI_DIV, "saxi_div", "saxi_mux", 0, 0x100, 0, 5, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_CFGAXI_DIV, "cfgaxi_div", "saxi_div", 0, 0x100, 5, 2, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_SD_DIV, "sd_div", "sd_mux", 0, 0x108, 0, 4, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_MMC1_DIV, "mmc1_div", "mmc1_mux", 0, 0x108, 5, 4, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_HSIC_DIV, "hsic_div", "rclk_hsic", 0, 0x130, 0, 2, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_MMC2_DIV, "mmc2_div", "mmc2_mux", 0, 0x140, 0, 4, CLK_DIVIDER_HIWORD_MASK, NULL, }, + { HI3620_MMC3_DIV, "mmc3_div", "mmc3_mux", 0, 0x140, 5, 4, CLK_DIVIDER_HIWORD_MASK, NULL, }, +}; + +static struct hisi_gate_clock hi3620_separated_gate_clks[] __initdata = { + { HI3620_TIMERCLK01, "timerclk01", "timer_rclk01", CLK_SET_RATE_PARENT, 0x20, 0, 0, }, + { HI3620_TIMER_RCLK01, "timer_rclk01", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x20, 1, 0, }, + { HI3620_TIMERCLK23, "timerclk23", "timer_rclk23", CLK_SET_RATE_PARENT, 0x20, 2, 0, }, + { HI3620_TIMER_RCLK23, "timer_rclk23", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x20, 3, 0, }, + { HI3620_RTCCLK, "rtcclk", "pclk", CLK_SET_RATE_PARENT, 0x20, 5, 0, }, + { HI3620_KPC_CLK, "kpc_clk", "pclk", CLK_SET_RATE_PARENT, 0x20, 6, 0, }, + { HI3620_GPIOCLK0, "gpioclk0", "pclk", CLK_SET_RATE_PARENT, 0x20, 8, 0, }, + { HI3620_GPIOCLK1, "gpioclk1", "pclk", CLK_SET_RATE_PARENT, 0x20, 9, 0, }, + { HI3620_GPIOCLK2, "gpioclk2", "pclk", CLK_SET_RATE_PARENT, 0x20, 10, 0, }, + { HI3620_GPIOCLK3, "gpioclk3", "pclk", CLK_SET_RATE_PARENT, 0x20, 11, 0, }, + { HI3620_GPIOCLK4, "gpioclk4", "pclk", CLK_SET_RATE_PARENT, 0x20, 12, 0, }, + { HI3620_GPIOCLK5, "gpioclk5", "pclk", CLK_SET_RATE_PARENT, 0x20, 13, 0, }, + { HI3620_GPIOCLK6, "gpioclk6", "pclk", CLK_SET_RATE_PARENT, 0x20, 14, 0, }, + { HI3620_GPIOCLK7, "gpioclk7", "pclk", CLK_SET_RATE_PARENT, 0x20, 15, 0, }, + { HI3620_GPIOCLK8, "gpioclk8", "pclk", CLK_SET_RATE_PARENT, 0x20, 16, 0, }, + { HI3620_GPIOCLK9, "gpioclk9", "pclk", CLK_SET_RATE_PARENT, 0x20, 17, 0, }, + { HI3620_GPIOCLK10, "gpioclk10", "pclk", CLK_SET_RATE_PARENT, 0x20, 18, 0, }, + { HI3620_GPIOCLK11, "gpioclk11", "pclk", CLK_SET_RATE_PARENT, 0x20, 19, 0, }, + { HI3620_GPIOCLK12, "gpioclk12", "pclk", CLK_SET_RATE_PARENT, 0x20, 20, 0, }, + { HI3620_GPIOCLK13, "gpioclk13", "pclk", CLK_SET_RATE_PARENT, 0x20, 21, 0, }, + { HI3620_GPIOCLK14, "gpioclk14", "pclk", CLK_SET_RATE_PARENT, 0x20, 22, 0, }, + { HI3620_GPIOCLK15, "gpioclk15", "pclk", CLK_SET_RATE_PARENT, 0x20, 23, 0, }, + { HI3620_GPIOCLK16, "gpioclk16", "pclk", CLK_SET_RATE_PARENT, 0x20, 24, 0, }, + { HI3620_GPIOCLK17, "gpioclk17", "pclk", CLK_SET_RATE_PARENT, 0x20, 25, 0, }, + { HI3620_GPIOCLK18, "gpioclk18", "pclk", CLK_SET_RATE_PARENT, 0x20, 26, 0, }, + { HI3620_GPIOCLK19, "gpioclk19", "pclk", CLK_SET_RATE_PARENT, 0x20, 27, 0, }, + { HI3620_GPIOCLK20, "gpioclk20", "pclk", CLK_SET_RATE_PARENT, 0x20, 28, 0, }, + { HI3620_GPIOCLK21, "gpioclk21", "pclk", CLK_SET_RATE_PARENT, 0x20, 29, 0, }, + { HI3620_DPHY0_CLK, "dphy0_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 15, 0, }, + { HI3620_DPHY1_CLK, "dphy1_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 16, 0, }, + { HI3620_DPHY2_CLK, "dphy2_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 17, 0, }, + { HI3620_USBPHY_CLK, "usbphy_clk", "rclk_pico", CLK_SET_RATE_PARENT, 0x30, 24, 0, }, + { HI3620_ACP_CLK, "acp_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x30, 28, 0, }, + { HI3620_TIMERCLK45, "timerclk45", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 3, 0, }, + { HI3620_TIMERCLK67, "timerclk67", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 4, 0, }, + { HI3620_TIMERCLK89, "timerclk89", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 5, 0, }, + { HI3620_PWMCLK0, "pwmclk0", "pwm0_mux", CLK_SET_RATE_PARENT, 0x40, 7, 0, }, + { HI3620_PWMCLK1, "pwmclk1", "pwm1_mux", CLK_SET_RATE_PARENT, 0x40, 8, 0, }, + { HI3620_UARTCLK0, "uartclk0", "uart0_mux", CLK_SET_RATE_PARENT, 0x40, 16, 0, }, + { HI3620_UARTCLK1, "uartclk1", "uart1_mux", CLK_SET_RATE_PARENT, 0x40, 17, 0, }, + { HI3620_UARTCLK2, "uartclk2", "uart2_mux", CLK_SET_RATE_PARENT, 0x40, 18, 0, }, + { HI3620_UARTCLK3, "uartclk3", "uart3_mux", CLK_SET_RATE_PARENT, 0x40, 19, 0, }, + { HI3620_UARTCLK4, "uartclk4", "uart4_mux", CLK_SET_RATE_PARENT, 0x40, 20, 0, }, + { HI3620_SPICLK0, "spiclk0", "spi0_mux", CLK_SET_RATE_PARENT, 0x40, 21, 0, }, + { HI3620_SPICLK1, "spiclk1", "spi1_mux", CLK_SET_RATE_PARENT, 0x40, 22, 0, }, + { HI3620_SPICLK2, "spiclk2", "spi2_mux", CLK_SET_RATE_PARENT, 0x40, 23, 0, }, + { HI3620_I2CCLK0, "i2cclk0", "pclk", CLK_SET_RATE_PARENT, 0x40, 24, 0, }, + { HI3620_I2CCLK1, "i2cclk1", "pclk", CLK_SET_RATE_PARENT, 0x40, 25, 0, }, + { HI3620_SCI_CLK, "sci_clk", "osc26m", CLK_SET_RATE_PARENT, 0x40, 26, 0, }, + { HI3620_I2CCLK2, "i2cclk2", "pclk", CLK_SET_RATE_PARENT, 0x40, 28, 0, }, + { HI3620_I2CCLK3, "i2cclk3", "pclk", CLK_SET_RATE_PARENT, 0x40, 29, 0, }, + { HI3620_DDRC_PER_CLK, "ddrc_per_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 9, 0, }, + { HI3620_DMAC_CLK, "dmac_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 10, 0, }, + { HI3620_USB2DVC_CLK, "usb2dvc_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 17, 0, }, + { HI3620_SD_CLK, "sd_clk", "sd_div", CLK_SET_RATE_PARENT, 0x50, 20, 0, }, + { HI3620_MMC_CLK1, "mmc_clk1", "mmc1_mux2", CLK_SET_RATE_PARENT, 0x50, 21, 0, }, + { HI3620_MMC_CLK2, "mmc_clk2", "mmc2_div", CLK_SET_RATE_PARENT, 0x50, 22, 0, }, + { HI3620_MMC_CLK3, "mmc_clk3", "mmc3_div", CLK_SET_RATE_PARENT, 0x50, 23, 0, }, + { HI3620_MCU_CLK, "mcu_clk", "acp_clk", CLK_SET_RATE_PARENT, 0x50, 24, 0, }, +}; + +static void __init hi3620_clk_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI3620_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks, + ARRAY_SIZE(hi3620_fixed_rate_clks), + clk_data); + hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks, + ARRAY_SIZE(hi3620_fixed_factor_clks), + clk_data); + hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks), + clk_data); + hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks), + clk_data); + hisi_clk_register_gate_sep(hi3620_separated_gate_clks, + ARRAY_SIZE(hi3620_separated_gate_clks), + clk_data); +} +CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init); + +struct hisi_mmc_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + u32 clken_reg; + u32 clken_bit; + u32 div_reg; + u32 div_off; + u32 div_bits; + u32 drv_reg; + u32 drv_off; + u32 drv_bits; + u32 sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +struct clk_mmc { + struct clk_hw hw; + u32 id; + void __iomem *clken_reg; + u32 clken_bit; + void __iomem *div_reg; + u32 div_off; + u32 div_bits; + void __iomem *drv_reg; + u32 drv_off; + u32 drv_bits; + void __iomem *sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw) + +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = { + { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4}, + { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4}, + { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4}, + { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4}, +}; + +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + switch (parent_rate) { + case 26000000: + return 13000000; + case 180000000: + return 25000000; + case 360000000: + return 50000000; + case 720000000: + return 100000000; + case 1440000000: + return 180000000; + default: + return parent_rate; + } +} + +static int mmc_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_mmc *mclk = to_mmc(hw); + + if ((req->rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) { + req->rate = 13000000; + req->best_parent_rate = 26000000; + } else if (req->rate <= 26000000) { + req->rate = 25000000; + req->best_parent_rate = 180000000; + } else if (req->rate <= 52000000) { + req->rate = 50000000; + req->best_parent_rate = 360000000; + } else if (req->rate <= 100000000) { + req->rate = 100000000; + req->best_parent_rate = 720000000; + } else { + /* max is 180M */ + req->rate = 180000000; + req->best_parent_rate = 1440000000; + } + return -EINVAL; +} + +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) { + if (para % 2) + val |= 1 << (off + i); + else + val &= ~(1 << (off + i)); + para = para >> 1; + } + + return val; +} + +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long flags; + u32 sam, drv, div, val; + static DEFINE_SPINLOCK(mmc_clk_lock); + + switch (rate) { + case 13000000: + sam = 3; + drv = 1; + div = 1; + break; + case 25000000: + sam = 13; + drv = 6; + div = 6; + break; + case 50000000: + sam = 3; + drv = 6; + div = 6; + break; + case 100000000: + sam = 6; + drv = 4; + div = 6; + break; + case 180000000: + sam = 6; + drv = 4; + div = 7; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&mmc_clk_lock, flags); + + val = readl_relaxed(mclk->clken_reg); + val &= ~(1 << mclk->clken_bit); + writel_relaxed(val, mclk->clken_reg); + + val = readl_relaxed(mclk->sam_reg); + val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits); + writel_relaxed(val, mclk->sam_reg); + + val = readl_relaxed(mclk->drv_reg); + val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits); + writel_relaxed(val, mclk->drv_reg); + + val = readl_relaxed(mclk->div_reg); + val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits); + writel_relaxed(val, mclk->div_reg); + + val = readl_relaxed(mclk->clken_reg); + val |= 1 << mclk->clken_bit; + writel_relaxed(val, mclk->clken_reg); + + spin_unlock_irqrestore(&mmc_clk_lock, flags); + + return 0; +} + +static int mmc_clk_prepare(struct clk_hw *hw) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long rate; + + if (mclk->id == HI3620_MMC_CIUCLK1) + rate = 13000000; + else + rate = 25000000; + + return mmc_clk_set_timing(hw, rate); +} + +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return mmc_clk_set_timing(hw, rate); +} + +static const struct clk_ops clk_mmc_ops = { + .prepare = mmc_clk_prepare, + .determine_rate = mmc_clk_determine_rate, + .set_rate = mmc_clk_set_rate, + .recalc_rate = mmc_clk_recalc_rate, +}; + +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk, + void __iomem *base, struct device_node *np) +{ + struct clk_mmc *mclk; + struct clk *clk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(*mclk), GFP_KERNEL); + if (!mclk) + return ERR_PTR(-ENOMEM); + + init.name = mmc_clk->name; + init.ops = &clk_mmc_ops; + init.flags = mmc_clk->flags | CLK_IS_BASIC; + init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL); + init.num_parents = (mmc_clk->parent_name ? 1 : 0); + mclk->hw.init = &init; + + mclk->id = mmc_clk->id; + mclk->clken_reg = base + mmc_clk->clken_reg; + mclk->clken_bit = mmc_clk->clken_bit; + mclk->div_reg = base + mmc_clk->div_reg; + mclk->div_off = mmc_clk->div_off; + mclk->div_bits = mmc_clk->div_bits; + mclk->drv_reg = base + mmc_clk->drv_reg; + mclk->drv_off = mmc_clk->drv_off; + mclk->drv_bits = mmc_clk->drv_bits; + mclk->sam_reg = base + mmc_clk->sam_reg; + mclk->sam_off = mmc_clk->sam_off; + mclk->sam_bits = mmc_clk->sam_bits; + + clk = clk_register(NULL, &mclk->hw); + if (WARN_ON(IS_ERR(clk))) + kfree(mclk); + return clk; +} + +static void __init hi3620_mmc_clk_init(struct device_node *node) +{ + void __iomem *base; + int i, num = ARRAY_SIZE(hi3620_mmc_clks); + struct clk_onecell_data *clk_data; + + if (!node) { + pr_err("failed to find pctrl node in DTS\n"); + return; + } + + base = of_iomap(node, 0); + if (!base) { + pr_err("failed to map pctrl\n"); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (WARN_ON(!clk_data)) + return; + + clk_data->clks = kcalloc(num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + return; + + for (i = 0; i < num; i++) { + struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i]; + clk_data->clks[mmc_clk->id] = + hisi_register_clk_mmc(mmc_clk, base, node); + } + + clk_data->clk_num = num; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init); diff --git a/drivers/clk/hisilicon/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c new file mode 100644 index 000000000..e8b2c43b1 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3660-stub.c @@ -0,0 +1,187 @@ +/* + * Hisilicon clock driver + * + * Copyright (c) 2013-2017 Hisilicon Limited. + * Copyright (c) 2017 Linaro Limited. + * + * Author: Kai Zhao <zhaokai1@hisilicon.com> + * Tao Wang <kevin.wangtao@hisilicon.com> + * Leo Yan <leo.yan@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <dt-bindings/clock/hi3660-clock.h> + +#define HI3660_STUB_CLOCK_DATA (0x70) +#define MHZ (1000 * 1000) + +#define DEFINE_CLK_STUB(_id, _cmd, _name) \ + { \ + .id = (_id), \ + .cmd = (_cmd), \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = &hi3660_stub_clk_ops, \ + .num_parents = 0, \ + .flags = CLK_GET_RATE_NOCACHE, \ + }, \ + }, + +#define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw) + +struct hi3660_stub_clk_chan { + struct mbox_client cl; + struct mbox_chan *mbox; +}; + +struct hi3660_stub_clk { + unsigned int id; + struct clk_hw hw; + unsigned int cmd; + unsigned int msg[8]; + unsigned int rate; +}; + +static void __iomem *freq_reg; +static struct hi3660_stub_clk_chan stub_clk_chan; + +static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); + + /* + * LPM3 writes back the CPU frequency in shared SRAM so read + * back the frequency. + */ + stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ; + return stub_clk->rate; +} + +static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + /* + * LPM3 handles rate rounding so just return whatever + * rate is requested. + */ + return rate; +} + +static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); + + stub_clk->msg[0] = stub_clk->cmd; + stub_clk->msg[1] = rate / MHZ; + + dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n", + stub_clk->msg[0], stub_clk->msg[1]); + + mbox_send_message(stub_clk_chan.mbox, stub_clk->msg); + mbox_client_txdone(stub_clk_chan.mbox, 0); + + stub_clk->rate = rate; + return 0; +} + +static const struct clk_ops hi3660_stub_clk_ops = { + .recalc_rate = hi3660_stub_clk_recalc_rate, + .round_rate = hi3660_stub_clk_round_rate, + .set_rate = hi3660_stub_clk_set_rate, +}; + +static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = { + DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0") + DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1") + DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d") + DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc") +}; + +static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + unsigned int idx = clkspec->args[0]; + + if (idx >= HI3660_CLK_STUB_NUM) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &hi3660_stub_clks[idx].hw; +} + +static int hi3660_stub_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + unsigned int i; + int ret; + + /* Use mailbox client without blocking */ + stub_clk_chan.cl.dev = dev; + stub_clk_chan.cl.tx_done = NULL; + stub_clk_chan.cl.tx_block = false; + stub_clk_chan.cl.knows_txdone = false; + + /* Allocate mailbox channel */ + stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0); + if (IS_ERR(stub_clk_chan.mbox)) + return PTR_ERR(stub_clk_chan.mbox); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + freq_reg = devm_ioremap(dev, res->start, resource_size(res)); + if (!freq_reg) + return -ENOMEM; + + freq_reg += HI3660_STUB_CLOCK_DATA; + + for (i = 0; i < HI3660_CLK_STUB_NUM; i++) { + ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get, + hi3660_stub_clks); +} + +static const struct of_device_id hi3660_stub_clk_of_match[] = { + { .compatible = "hisilicon,hi3660-stub-clk", }, + {} +}; + +static struct platform_driver hi3660_stub_clk_driver = { + .probe = hi3660_stub_clk_probe, + .driver = { + .name = "hi3660-stub-clk", + .of_match_table = hi3660_stub_clk_of_match, + }, +}; + +static int __init hi3660_stub_clk_init(void) +{ + return platform_driver_register(&hi3660_stub_clk_driver); +} +subsys_initcall(hi3660_stub_clk_init); diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c new file mode 100644 index 000000000..794eeff0d --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3660.c @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <dt-bindings/clock/hi3660-clock.h> +#include <linux/clk-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" + +static const struct hisi_fixed_rate_clock hi3660_fixed_rate_clks[] = { + { HI3660_CLKIN_SYS, "clkin_sys", NULL, 0, 19200000, }, + { HI3660_CLKIN_REF, "clkin_ref", NULL, 0, 32764, }, + { HI3660_CLK_FLL_SRC, "clk_fll_src", NULL, 0, 128000000, }, + { HI3660_CLK_PPLL0, "clk_ppll0", NULL, 0, 1600000000, }, + { HI3660_CLK_PPLL1, "clk_ppll1", NULL, 0, 1866000000, }, + { HI3660_CLK_PPLL2, "clk_ppll2", NULL, 0, 2880000000UL, }, + { HI3660_CLK_PPLL3, "clk_ppll3", NULL, 0, 1290000000, }, + { HI3660_CLK_SCPLL, "clk_scpll", NULL, 0, 245760000, }, + { HI3660_PCLK, "pclk", NULL, 0, 20000000, }, + { HI3660_CLK_UART0_DBG, "clk_uart0_dbg", NULL, 0, 19200000, }, + { HI3660_CLK_UART6, "clk_uart6", NULL, 0, 19200000, }, + { HI3660_OSC32K, "osc32k", NULL, 0, 32764, }, + { HI3660_OSC19M, "osc19m", NULL, 0, 19200000, }, + { HI3660_CLK_480M, "clk_480m", NULL, 0, 480000000, }, + { HI3660_CLK_INV, "clk_inv", NULL, 0, 10000000, }, +}; + +/* crgctrl */ +static const struct hisi_fixed_factor_clock hi3660_crg_fixed_factor_clks[] = { + { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 16, 0, }, + { HI3660_CLK_FACTOR_MMC, "clk_factor_mmc", "clkin_sys", 1, 6, 0, }, + { HI3660_CLK_GATE_I2C0, "clk_gate_i2c0", "clk_i2c0_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C1, "clk_gate_i2c1", "clk_i2c1_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C2, "clk_gate_i2c2", "clk_i2c2_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C6, "clk_gate_i2c6", "clk_i2c6_iomcu", 1, 4, 0, }, + { HI3660_CLK_DIV_SYSBUS, "clk_div_sysbus", "clk_mux_sysbus", 1, 7, 0, }, + { HI3660_CLK_DIV_320M, "clk_div_320m", "clk_320m_pll_gt", 1, 5, 0, }, + { HI3660_CLK_DIV_A53, "clk_div_a53hpm", "clk_a53hpm_andgt", 1, 6, 0, }, + { HI3660_CLK_GATE_SPI0, "clk_gate_spi0", "clk_ppll0", 1, 8, 0, }, + { HI3660_CLK_GATE_SPI2, "clk_gate_spi2", "clk_ppll0", 1, 8, 0, }, + { HI3660_PCIEPHY_REF, "clk_pciephy_ref", "clk_div_pciephy", 1, 1, 0, }, + { HI3660_CLK_ABB_USB, "clk_abb_usb", "clk_gate_usb_tcxo_en", 1, 1, 0 }, + { HI3660_VENC_VOLT_HOLD, "venc_volt_hold", "peri_volt_hold", 1, 1, 0, }, + { HI3660_CLK_FAC_ISP_SNCLK, "clk_isp_snclk_fac", "clk_isp_snclk_angt", + 1, 10, 0, }, +}; + +static const struct hisi_gate_clock hi3660_crgctrl_gate_sep_clks[] = { + { HI3660_PERI_VOLT_HOLD, "peri_volt_hold", "clkin_sys", + CLK_SET_RATE_PARENT, 0x0, 0, 0, }, + { HI3660_HCLK_GATE_SDIO0, "hclk_gate_sdio0", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x0, 21, 0, }, + { HI3660_HCLK_GATE_SD, "hclk_gate_sd", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x0, 30, 0, }, + { HI3660_CLK_GATE_AOMM, "clk_gate_aomm", "clk_div_aomm", + CLK_SET_RATE_PARENT, 0x0, 31, 0, }, + { HI3660_PCLK_GPIO0, "pclk_gpio0", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 0, 0, }, + { HI3660_PCLK_GPIO1, "pclk_gpio1", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 1, 0, }, + { HI3660_PCLK_GPIO2, "pclk_gpio2", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 2, 0, }, + { HI3660_PCLK_GPIO3, "pclk_gpio3", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 3, 0, }, + { HI3660_PCLK_GPIO4, "pclk_gpio4", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 4, 0, }, + { HI3660_PCLK_GPIO5, "pclk_gpio5", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 5, 0, }, + { HI3660_PCLK_GPIO6, "pclk_gpio6", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 6, 0, }, + { HI3660_PCLK_GPIO7, "pclk_gpio7", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 7, 0, }, + { HI3660_PCLK_GPIO8, "pclk_gpio8", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 8, 0, }, + { HI3660_PCLK_GPIO9, "pclk_gpio9", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 9, 0, }, + { HI3660_PCLK_GPIO10, "pclk_gpio10", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 10, 0, }, + { HI3660_PCLK_GPIO11, "pclk_gpio11", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 11, 0, }, + { HI3660_PCLK_GPIO12, "pclk_gpio12", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 12, 0, }, + { HI3660_PCLK_GPIO13, "pclk_gpio13", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 13, 0, }, + { HI3660_PCLK_GPIO14, "pclk_gpio14", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 14, 0, }, + { HI3660_PCLK_GPIO15, "pclk_gpio15", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 15, 0, }, + { HI3660_PCLK_GPIO16, "pclk_gpio16", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 16, 0, }, + { HI3660_PCLK_GPIO17, "pclk_gpio17", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 17, 0, }, + { HI3660_PCLK_GPIO18, "pclk_gpio18", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 18, 0, }, + { HI3660_PCLK_GPIO19, "pclk_gpio19", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 19, 0, }, + { HI3660_PCLK_GPIO20, "pclk_gpio20", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 20, 0, }, + { HI3660_PCLK_GPIO21, "pclk_gpio21", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 21, 0, }, + { HI3660_CLK_GATE_SPI3, "clk_gate_spi3", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 30, 0, }, + { HI3660_CLK_GATE_I2C7, "clk_gate_i2c7", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x10, 31, 0, }, + { HI3660_CLK_GATE_I2C3, "clk_gate_i2c3", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x20, 7, 0, }, + { HI3660_CLK_GATE_SPI1, "clk_gate_spi1", "clk_mux_spi", + CLK_SET_RATE_PARENT, 0x20, 9, 0, }, + { HI3660_CLK_GATE_UART1, "clk_gate_uart1", "clk_mux_uarth", + CLK_SET_RATE_PARENT, 0x20, 11, 0, }, + { HI3660_CLK_GATE_UART2, "clk_gate_uart2", "clk_mux_uart1", + CLK_SET_RATE_PARENT, 0x20, 12, 0, }, + { HI3660_CLK_GATE_UART4, "clk_gate_uart4", "clk_mux_uarth", + CLK_SET_RATE_PARENT, 0x20, 14, 0, }, + { HI3660_CLK_GATE_UART5, "clk_gate_uart5", "clk_mux_uart1", + CLK_SET_RATE_PARENT, 0x20, 15, 0, }, + { HI3660_CLK_GATE_I2C4, "clk_gate_i2c4", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x20, 27, 0, }, + { HI3660_CLK_GATE_DMAC, "clk_gate_dmac", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x30, 1, 0, }, + { HI3660_CLK_GATE_VENC, "clk_gate_venc", "clk_div_venc", + CLK_SET_RATE_PARENT, 0x30, 10, 0, }, + { HI3660_CLK_GATE_VDEC, "clk_gate_vdec", "clk_div_vdec", + CLK_SET_RATE_PARENT, 0x30, 11, 0, }, + { HI3660_PCLK_GATE_DSS, "pclk_gate_dss", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x30, 12, 0, }, + { HI3660_ACLK_GATE_DSS, "aclk_gate_dss", "clk_gate_vivobus", + CLK_SET_RATE_PARENT, 0x30, 13, 0, }, + { HI3660_CLK_GATE_LDI1, "clk_gate_ldi1", "clk_div_ldi1", + CLK_SET_RATE_PARENT, 0x30, 14, 0, }, + { HI3660_CLK_GATE_LDI0, "clk_gate_ldi0", "clk_div_ldi0", + CLK_SET_RATE_PARENT, 0x30, 15, 0, }, + { HI3660_CLK_GATE_VIVOBUS, "clk_gate_vivobus", "clk_div_vivobus", + CLK_SET_RATE_PARENT, 0x30, 16, 0, }, + { HI3660_CLK_GATE_EDC0, "clk_gate_edc0", "clk_div_edc0", + CLK_SET_RATE_PARENT, 0x30, 17, 0, }, + { HI3660_CLK_GATE_TXDPHY0_CFG, "clk_gate_txdphy0_cfg", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 28, 0, }, + { HI3660_CLK_GATE_TXDPHY0_REF, "clk_gate_txdphy0_ref", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 29, 0, }, + { HI3660_CLK_GATE_TXDPHY1_CFG, "clk_gate_txdphy1_cfg", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 30, 0, }, + { HI3660_CLK_GATE_TXDPHY1_REF, "clk_gate_txdphy1_ref", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 31, 0, }, + { HI3660_ACLK_GATE_USB3OTG, "aclk_gate_usb3otg", "clk_div_mmc0bus", + CLK_SET_RATE_PARENT, 0x40, 1, 0, }, + { HI3660_CLK_GATE_SPI4, "clk_gate_spi4", "clk_mux_spi", + CLK_SET_RATE_PARENT, 0x40, 4, 0, }, + { HI3660_CLK_GATE_SD, "clk_gate_sd", "clk_mux_sd_sys", + CLK_SET_RATE_PARENT, 0x40, 17, 0, }, + { HI3660_CLK_GATE_SDIO0, "clk_gate_sdio0", "clk_mux_sdio_sys", + CLK_SET_RATE_PARENT, 0x40, 19, 0, }, + { HI3660_CLK_GATE_ISP_SNCLK0, "clk_gate_isp_snclk0", + "clk_isp_snclk_mux", CLK_SET_RATE_PARENT, 0x50, 16, 0, }, + { HI3660_CLK_GATE_ISP_SNCLK1, "clk_gate_isp_snclk1", + "clk_isp_snclk_mux", CLK_SET_RATE_PARENT, 0x50, 17, 0, }, + { HI3660_CLK_GATE_ISP_SNCLK2, "clk_gate_isp_snclk2", + "clk_isp_snclk_mux", CLK_SET_RATE_PARENT, 0x50, 18, 0, }, + /* + * clk_gate_ufs_subsys is a system bus clock, mark it as critical + * clock and keep it on for system suspend and resume. + */ + { HI3660_CLK_GATE_UFS_SUBSYS, "clk_gate_ufs_subsys", "clk_div_sysbus", + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0x50, 21, 0, }, + { HI3660_PCLK_GATE_DSI0, "pclk_gate_dsi0", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x50, 28, 0, }, + { HI3660_PCLK_GATE_DSI1, "pclk_gate_dsi1", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x50, 29, 0, }, + { HI3660_ACLK_GATE_PCIE, "aclk_gate_pcie", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 5, 0, }, + { HI3660_PCLK_GATE_PCIE_SYS, "pclk_gate_pcie_sys", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 7, 0, }, + { HI3660_CLK_GATE_PCIEAUX, "clk_gate_pcieaux", "clkin_sys", + CLK_SET_RATE_PARENT, 0x420, 8, 0, }, + { HI3660_PCLK_GATE_PCIE_PHY, "pclk_gate_pcie_phy", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 9, 0, }, +}; + +static const struct hisi_gate_clock hi3660_crgctrl_gate_clks[] = { + { HI3660_CLK_ANDGT_LDI0, "clk_andgt_ldi0", "clk_mux_ldi0", + CLK_SET_RATE_PARENT, 0xf0, 6, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_LDI1, "clk_andgt_ldi1", "clk_mux_ldi1", + CLK_SET_RATE_PARENT, 0xf0, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_EDC0, "clk_andgt_edc0", "clk_mux_edc0", + CLK_SET_RATE_PARENT, 0xf0, 8, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_VDEC, "clk_andgt_vdec", "clk_mux_vdec", + CLK_SET_RATE_PARENT, 0xf0, 15, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_VENC, "clk_andgt_venc", "clk_mux_venc", + CLK_SET_RATE_PARENT, 0xf4, 0, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_GATE_UFSPHY_GT, "clk_gate_ufsphy_gt", "clk_div_ufsperi", + CLK_SET_RATE_PARENT, 0xf4, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_MMC, "clk_andgt_mmc", "clk_mux_mmc_pll", + CLK_SET_RATE_PARENT, 0xf4, 2, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SD, "clk_andgt_sd", "clk_mux_sd_pll", + CLK_SET_RATE_PARENT, 0xf4, 3, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_A53HPM_ANDGT, "clk_a53hpm_andgt", "clk_mux_a53hpm", + CLK_SET_RATE_PARENT, 0xf4, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SDIO, "clk_andgt_sdio", "clk_mux_sdio_pll", + CLK_SET_RATE_PARENT, 0xf4, 8, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UART0, "clk_andgt_uart0", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 9, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UART1, "clk_andgt_uart1", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 10, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UARTH, "clk_andgt_uarth", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 11, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SPI, "clk_andgt_spi", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 13, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_VIVOBUS_ANDGT, "clk_vivobus_andgt", "clk_mux_vivobus", + CLK_SET_RATE_PARENT, 0xf8, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_AOMM_ANDGT, "clk_aomm_andgt", "clk_ppll2", + CLK_SET_RATE_PARENT, 0xf8, 3, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_320M_PLL_GT, "clk_320m_pll_gt", "clk_mux_320m", + CLK_SET_RATE_PARENT, 0xf8, 10, 0, }, + { HI3660_CLK_ANGT_ISP_SNCLK, "clk_isp_snclk_angt", "clk_div_a53hpm", + CLK_SET_RATE_PARENT, 0x108, 2, CLK_GATE_HIWORD_MASK, }, + { HI3660_AUTODIV_EMMC0BUS, "autodiv_emmc0bus", "autodiv_sysbus", + CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_AUTODIV_SYSBUS, "autodiv_sysbus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_GATE_UFSPHY_CFG, "clk_gate_ufsphy_cfg", + "clk_div_ufsphy_cfg", CLK_SET_RATE_PARENT, 0x420, 12, 0, }, + { HI3660_CLK_GATE_UFSIO_REF, "clk_gate_ufsio_ref", + "clk_gate_ufs_tcxo_en", CLK_SET_RATE_PARENT, 0x420, 14, 0, }, +}; + +static const char *const +clk_mux_sysbus_p[] = {"clk_ppll1", "clk_ppll0"}; +static const char *const +clk_mux_sdio_sys_p[] = {"clk_factor_mmc", "clk_div_sdio",}; +static const char *const +clk_mux_sd_sys_p[] = {"clk_factor_mmc", "clk_div_sd",}; +static const char *const +clk_mux_pll_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll2",}; +static const char *const +clk_mux_pll0123_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll3",}; +static const char *const +clk_mux_edc0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll1", "clk_inv", + "clk_ppll2", "clk_inv", "clk_inv", "clk_inv", + "clk_ppll3", "clk_inv", "clk_inv", "clk_inv", + "clk_inv", "clk_inv", "clk_inv", "clk_inv",}; +static const char *const +clk_mux_ldi0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll2", "clk_inv", + "clk_ppll1", "clk_inv", "clk_inv", "clk_inv", + "clk_ppll3", "clk_inv", "clk_inv", "clk_inv", + "clk_inv", "clk_inv", "clk_inv", "clk_inv",}; +static const char *const +clk_mux_uart0_p[] = {"clkin_sys", "clk_div_uart0",}; +static const char *const +clk_mux_uart1_p[] = {"clkin_sys", "clk_div_uart1",}; +static const char *const +clk_mux_uarth_p[] = {"clkin_sys", "clk_div_uarth",}; +static const char *const +clk_mux_pll02p[] = {"clk_ppll0", "clk_ppll2",}; +static const char *const +clk_mux_ioperi_p[] = {"clk_div_320m", "clk_div_a53hpm",}; +static const char *const +clk_mux_spi_p[] = {"clkin_sys", "clk_div_spi",}; +static const char *const +clk_mux_i2c_p[] = {"clkin_sys", "clk_div_i2c",}; +static const char *const +clk_mux_venc_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll3", "clk_ppll3",}; +static const char *const +clk_mux_isp_snclk_p[] = {"clkin_sys", "clk_isp_snclk_div"}; + +static const struct hisi_mux_clock hi3660_crgctrl_mux_clks[] = { + { HI3660_CLK_MUX_SYSBUS, "clk_mux_sysbus", clk_mux_sysbus_p, + ARRAY_SIZE(clk_mux_sysbus_p), CLK_SET_RATE_PARENT, 0xac, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UART0, "clk_mux_uart0", clk_mux_uart0_p, + ARRAY_SIZE(clk_mux_uart0_p), CLK_SET_RATE_PARENT, 0xac, 2, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UART1, "clk_mux_uart1", clk_mux_uart1_p, + ARRAY_SIZE(clk_mux_uart1_p), CLK_SET_RATE_PARENT, 0xac, 3, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UARTH, "clk_mux_uarth", clk_mux_uarth_p, + ARRAY_SIZE(clk_mux_uarth_p), CLK_SET_RATE_PARENT, 0xac, 4, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SPI, "clk_mux_spi", clk_mux_spi_p, + ARRAY_SIZE(clk_mux_spi_p), CLK_SET_RATE_PARENT, 0xac, 8, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_I2C, "clk_mux_i2c", clk_mux_i2c_p, + ARRAY_SIZE(clk_mux_i2c_p), CLK_SET_RATE_PARENT, 0xac, 13, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_MMC_PLL, "clk_mux_mmc_pll", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xb4, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_LDI1, "clk_mux_ldi1", clk_mux_ldi0_p, + ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 8, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_LDI0, "clk_mux_ldi0", clk_mux_ldi0_p, + ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 12, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SD_PLL, "clk_mux_sd_pll", clk_mux_pll_p, + ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xb8, 4, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SD_SYS, "clk_mux_sd_sys", clk_mux_sd_sys_p, + ARRAY_SIZE(clk_mux_sd_sys_p), CLK_SET_RATE_PARENT, 0xb8, 6, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_EDC0, "clk_mux_edc0", clk_mux_edc0_p, + ARRAY_SIZE(clk_mux_edc0_p), CLK_SET_RATE_PARENT, 0xbc, 6, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SDIO_SYS, "clk_mux_sdio_sys", clk_mux_sdio_sys_p, + ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xc0, 6, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SDIO_PLL, "clk_mux_sdio_pll", clk_mux_pll_p, + ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xc0, 4, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_VENC, "clk_mux_venc", clk_mux_venc_p, + ARRAY_SIZE(clk_mux_venc_p), CLK_SET_RATE_PARENT, 0xc8, 11, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_VDEC, "clk_mux_vdec", clk_mux_pll0123_p, + ARRAY_SIZE(clk_mux_pll0123_p), CLK_SET_RATE_PARENT, 0xcc, 5, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_VIVOBUS, "clk_mux_vivobus", clk_mux_pll0123_p, + ARRAY_SIZE(clk_mux_pll0123_p), CLK_SET_RATE_PARENT, 0xd0, 12, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_A53HPM, "clk_mux_a53hpm", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xd4, 9, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_320M, "clk_mux_320m", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0x100, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_ISP_SNCLK, "clk_isp_snclk_mux", clk_mux_isp_snclk_p, + ARRAY_SIZE(clk_mux_isp_snclk_p), CLK_SET_RATE_PARENT, 0x108, 3, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_IOPERI, "clk_mux_ioperi", clk_mux_ioperi_p, + ARRAY_SIZE(clk_mux_ioperi_p), CLK_SET_RATE_PARENT, 0x108, 10, 1, + CLK_MUX_HIWORD_MASK, }, +}; + +static const struct hisi_divider_clock hi3660_crgctrl_divider_clks[] = { + { HI3660_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0", + CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UART1, "clk_div_uart1", "clk_andgt_uart1", + CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth", + CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC, "clk_div_mmc", "clk_andgt_mmc", + CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd", + CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0", + CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0", + CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio", + CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1", + CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi", + CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_VENC, "clk_div_venc", "clk_andgt_venc", + CLK_SET_RATE_PARENT, 0xc8, 6, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_VDEC, "clk_div_vdec", "clk_andgt_vdec", + CLK_SET_RATE_PARENT, 0xcc, 0, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_vivobus_andgt", + CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UFSPHY, "clk_div_ufsphy_cfg", "clk_gate_ufsphy_gt", + CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus", + CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UFSPERI, "clk_div_ufsperi", "clk_gate_ufs_subsys", + CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_AOMM, "clk_div_aomm", "clk_aomm_andgt", + CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_ISP_SNCLK, "clk_isp_snclk_div", "clk_isp_snclk_fac", + CLK_SET_RATE_PARENT, 0x108, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_mux_ioperi", + CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +/* clk_pmuctrl */ +/* pmu register need shift 2 bits */ +static const struct hisi_gate_clock hi3660_pmu_gate_clks[] = { + { HI3660_GATE_ABB_192, "clk_gate_abb_192", "clkin_sys", + CLK_SET_RATE_PARENT, (0x10a << 2), 3, 0, }, +}; + +/* clk_pctrl */ +static const struct hisi_gate_clock hi3660_pctrl_gate_clks[] = { + { HI3660_GATE_UFS_TCXO_EN, "clk_gate_ufs_tcxo_en", + "clk_gate_abb_192", CLK_SET_RATE_PARENT, 0x10, 0, + CLK_GATE_HIWORD_MASK, }, + { HI3660_GATE_USB_TCXO_EN, "clk_gate_usb_tcxo_en", "clk_gate_abb_192", + CLK_SET_RATE_PARENT, 0x10, 1, CLK_GATE_HIWORD_MASK, }, +}; + +/* clk_sctrl */ +static const struct hisi_gate_clock hi3660_sctrl_gate_sep_clks[] = { + { HI3660_PCLK_AO_GPIO0, "pclk_ao_gpio0", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 11, 0, }, + { HI3660_PCLK_AO_GPIO1, "pclk_ao_gpio1", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 12, 0, }, + { HI3660_PCLK_AO_GPIO2, "pclk_ao_gpio2", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 13, 0, }, + { HI3660_PCLK_AO_GPIO3, "pclk_ao_gpio3", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 14, 0, }, + { HI3660_PCLK_AO_GPIO4, "pclk_ao_gpio4", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 21, 0, }, + { HI3660_PCLK_AO_GPIO5, "pclk_ao_gpio5", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 22, 0, }, + { HI3660_PCLK_AO_GPIO6, "pclk_ao_gpio6", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 25, 0, }, + { HI3660_PCLK_GATE_MMBUF, "pclk_gate_mmbuf", "pclk_div_mmbuf", + CLK_SET_RATE_PARENT, 0x170, 23, 0, }, + { HI3660_CLK_GATE_DSS_AXI_MM, "clk_gate_dss_axi_mm", "aclk_mux_mmbuf", + CLK_SET_RATE_PARENT, 0x170, 24, 0, }, +}; + +static const struct hisi_gate_clock hi3660_sctrl_gate_clks[] = { + { HI3660_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "clk_sw_mmbuf", + CLK_SET_RATE_PARENT, 0x258, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_FLL_MMBUF_ANDGT, "clk_fll_mmbuf_andgt", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_SYS_MMBUF_ANDGT, "clk_sys_mmbuf_andgt", "clkin_sys", + CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_GATE_PCIEPHY_GT, "clk_gate_pciephy_gt", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +static const char *const +aclk_mux_mmbuf_p[] = {"aclk_div_mmbuf", "clk_gate_aomm",}; +static const char *const +clk_sw_mmbuf_p[] = {"clk_sys_mmbuf_andgt", "clk_fll_mmbuf_andgt", + "aclk_mux_mmbuf", "aclk_mux_mmbuf"}; + +static const struct hisi_mux_clock hi3660_sctrl_mux_clks[] = { + { HI3660_ACLK_MUX_MMBUF, "aclk_mux_mmbuf", aclk_mux_mmbuf_p, + ARRAY_SIZE(aclk_mux_mmbuf_p), CLK_SET_RATE_PARENT, 0x250, 12, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_SW_MMBUF, "clk_sw_mmbuf", clk_sw_mmbuf_p, + ARRAY_SIZE(clk_sw_mmbuf_p), CLK_SET_RATE_PARENT, 0x258, 8, 2, + CLK_MUX_HIWORD_MASK, }, +}; + +static const struct hisi_divider_clock hi3660_sctrl_divider_clks[] = { + { HI3660_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt", + CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt", + CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_PCIEPHY, "clk_div_pciephy", "clk_gate_pciephy_gt", + CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +/* clk_iomcu */ +static const struct hisi_gate_clock hi3660_iomcu_gate_sep_clks[] = { + { HI3660_CLK_I2C0_IOMCU, "clk_i2c0_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 3, 0, }, + { HI3660_CLK_I2C1_IOMCU, "clk_i2c1_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 4, 0, }, + { HI3660_CLK_I2C2_IOMCU, "clk_i2c2_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 5, 0, }, + { HI3660_CLK_I2C6_IOMCU, "clk_i2c6_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 27, 0, }, + { HI3660_CLK_IOMCU_PERI0, "iomcu_peri0", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x90, 0, 0, }, +}; + +static struct hisi_clock_data *clk_crgctrl_data; + +static void hi3660_clk_iomcu_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_iomcu_gate_sep_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi3660_iomcu_gate_sep_clks, + ARRAY_SIZE(hi3660_iomcu_gate_sep_clks), + clk_data); +} + +static void hi3660_clk_pmuctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_pmu_gate_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_gate(hi3660_pmu_gate_clks, + ARRAY_SIZE(hi3660_pmu_gate_clks), clk_data); +} + +static void hi3660_clk_pctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_pctrl_gate_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + hisi_clk_register_gate(hi3660_pctrl_gate_clks, + ARRAY_SIZE(hi3660_pctrl_gate_clks), clk_data); +} + +static void hi3660_clk_sctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_sctrl_gate_clks) + + ARRAY_SIZE(hi3660_sctrl_gate_sep_clks) + + ARRAY_SIZE(hi3660_sctrl_mux_clks) + + ARRAY_SIZE(hi3660_sctrl_divider_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + hisi_clk_register_gate(hi3660_sctrl_gate_clks, + ARRAY_SIZE(hi3660_sctrl_gate_clks), clk_data); + hisi_clk_register_gate_sep(hi3660_sctrl_gate_sep_clks, + ARRAY_SIZE(hi3660_sctrl_gate_sep_clks), + clk_data); + hisi_clk_register_mux(hi3660_sctrl_mux_clks, + ARRAY_SIZE(hi3660_sctrl_mux_clks), clk_data); + hisi_clk_register_divider(hi3660_sctrl_divider_clks, + ARRAY_SIZE(hi3660_sctrl_divider_clks), + clk_data); +} + +static void hi3660_clk_crgctrl_early_init(struct device_node *np) +{ + int nr = ARRAY_SIZE(hi3660_fixed_rate_clks) + + ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks) + + ARRAY_SIZE(hi3660_crgctrl_gate_clks) + + ARRAY_SIZE(hi3660_crgctrl_mux_clks) + + ARRAY_SIZE(hi3660_crg_fixed_factor_clks) + + ARRAY_SIZE(hi3660_crgctrl_divider_clks); + int i; + + clk_crgctrl_data = hisi_clk_init(np, nr); + if (!clk_crgctrl_data) + return; + + for (i = 0; i < nr; i++) + clk_crgctrl_data->clk_data.clks[i] = ERR_PTR(-EPROBE_DEFER); + + hisi_clk_register_fixed_rate(hi3660_fixed_rate_clks, + ARRAY_SIZE(hi3660_fixed_rate_clks), + clk_crgctrl_data); +} +CLK_OF_DECLARE_DRIVER(hi3660_clk_crgctrl, "hisilicon,hi3660-crgctrl", + hi3660_clk_crgctrl_early_init); + +static void hi3660_clk_crgctrl_init(struct device_node *np) +{ + struct clk **clks; + int i; + + if (!clk_crgctrl_data) + hi3660_clk_crgctrl_early_init(np); + + /* clk_crgctrl_data initialization failed */ + if (!clk_crgctrl_data) + return; + + hisi_clk_register_gate_sep(hi3660_crgctrl_gate_sep_clks, + ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks), + clk_crgctrl_data); + hisi_clk_register_gate(hi3660_crgctrl_gate_clks, + ARRAY_SIZE(hi3660_crgctrl_gate_clks), + clk_crgctrl_data); + hisi_clk_register_mux(hi3660_crgctrl_mux_clks, + ARRAY_SIZE(hi3660_crgctrl_mux_clks), + clk_crgctrl_data); + hisi_clk_register_fixed_factor(hi3660_crg_fixed_factor_clks, + ARRAY_SIZE(hi3660_crg_fixed_factor_clks), + clk_crgctrl_data); + hisi_clk_register_divider(hi3660_crgctrl_divider_clks, + ARRAY_SIZE(hi3660_crgctrl_divider_clks), + clk_crgctrl_data); + + clks = clk_crgctrl_data->clk_data.clks; + for (i = 0; i < clk_crgctrl_data->clk_data.clk_num; i++) { + if (IS_ERR(clks[i]) && PTR_ERR(clks[i]) != -EPROBE_DEFER) + pr_err("Failed to register crgctrl clock[%d] err=%ld\n", + i, PTR_ERR(clks[i])); + } +} + +static const struct of_device_id hi3660_clk_match_table[] = { + { .compatible = "hisilicon,hi3660-crgctrl", + .data = hi3660_clk_crgctrl_init }, + { .compatible = "hisilicon,hi3660-pctrl", + .data = hi3660_clk_pctrl_init }, + { .compatible = "hisilicon,hi3660-pmuctrl", + .data = hi3660_clk_pmuctrl_init }, + { .compatible = "hisilicon,hi3660-sctrl", + .data = hi3660_clk_sctrl_init }, + { .compatible = "hisilicon,hi3660-iomcu", + .data = hi3660_clk_iomcu_init }, + { } +}; + +static int hi3660_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + void (*init_func)(struct device_node *np); + + init_func = of_device_get_match_data(dev); + if (!init_func) + return -ENODEV; + + init_func(np); + + return 0; +} + +static struct platform_driver hi3660_clk_driver = { + .probe = hi3660_clk_probe, + .driver = { + .name = "hi3660-clk", + .of_match_table = hi3660_clk_match_table, + }, +}; + +static int __init hi3660_clk_init(void) +{ + return platform_driver_register(&hi3660_clk_driver); +} +core_initcall(hi3660_clk_init); diff --git a/drivers/clk/hisilicon/clk-hi6220-stub.c b/drivers/clk/hisilicon/clk-hi6220-stub.c new file mode 100644 index 000000000..329a09214 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220-stub.c @@ -0,0 +1,276 @@ +/* + * Hi6220 stub clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2015 Linaro Limited. + * + * Author: Leo Yan <leo.yan@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/mailbox_client.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +/* Stub clocks id */ +#define HI6220_STUB_ACPU0 0 +#define HI6220_STUB_ACPU1 1 +#define HI6220_STUB_GPU 2 +#define HI6220_STUB_DDR 5 + +/* Mailbox message */ +#define HI6220_MBOX_MSG_LEN 8 + +#define HI6220_MBOX_FREQ 0xA +#define HI6220_MBOX_CMD_SET 0x3 +#define HI6220_MBOX_OBJ_AP 0x0 + +/* CPU dynamic frequency scaling */ +#define ACPU_DFS_FREQ_MAX 0x1724 +#define ACPU_DFS_CUR_FREQ 0x17CC +#define ACPU_DFS_FLAG 0x1B30 +#define ACPU_DFS_FREQ_REQ 0x1B34 +#define ACPU_DFS_FREQ_LMT 0x1B38 +#define ACPU_DFS_LOCK_FLAG 0xAEAEAEAE + +#define to_stub_clk(hw) container_of(hw, struct hi6220_stub_clk, hw) + +struct hi6220_stub_clk { + u32 id; + + struct device *dev; + struct clk_hw hw; + + struct regmap *dfs_map; + struct mbox_client cl; + struct mbox_chan *mbox; +}; + +struct hi6220_mbox_msg { + unsigned char type; + unsigned char cmd; + unsigned char obj; + unsigned char src; + unsigned char para[4]; +}; + +union hi6220_mbox_data { + unsigned int data[HI6220_MBOX_MSG_LEN]; + struct hi6220_mbox_msg msg; +}; + +static unsigned int hi6220_acpu_get_freq(struct hi6220_stub_clk *stub_clk) +{ + unsigned int freq; + + regmap_read(stub_clk->dfs_map, ACPU_DFS_CUR_FREQ, &freq); + return freq; +} + +static int hi6220_acpu_set_freq(struct hi6220_stub_clk *stub_clk, + unsigned int freq) +{ + union hi6220_mbox_data data; + + /* set the frequency in sram */ + regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, freq); + + /* compound mailbox message */ + data.msg.type = HI6220_MBOX_FREQ; + data.msg.cmd = HI6220_MBOX_CMD_SET; + data.msg.obj = HI6220_MBOX_OBJ_AP; + data.msg.src = HI6220_MBOX_OBJ_AP; + + mbox_send_message(stub_clk->mbox, &data); + return 0; +} + +static int hi6220_acpu_round_freq(struct hi6220_stub_clk *stub_clk, + unsigned int freq) +{ + unsigned int limit_flag, limit_freq = UINT_MAX; + unsigned int max_freq; + + /* check the constrained frequency */ + regmap_read(stub_clk->dfs_map, ACPU_DFS_FLAG, &limit_flag); + if (limit_flag == ACPU_DFS_LOCK_FLAG) + regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, &limit_freq); + + /* check the supported maximum frequency */ + regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_MAX, &max_freq); + + /* calculate the real maximum frequency */ + max_freq = min(max_freq, limit_freq); + + if (WARN_ON(freq > max_freq)) + freq = max_freq; + + return freq; +} + +static unsigned long hi6220_stub_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 rate = 0; + struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); + + switch (stub_clk->id) { + case HI6220_STUB_ACPU0: + rate = hi6220_acpu_get_freq(stub_clk); + + /* convert from kHz to Hz */ + rate *= 1000; + break; + + default: + dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", + __func__, stub_clk->id); + break; + } + + return rate; +} + +static int hi6220_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); + unsigned long new_rate = rate / 1000; /* kHz */ + int ret = 0; + + switch (stub_clk->id) { + case HI6220_STUB_ACPU0: + ret = hi6220_acpu_set_freq(stub_clk, new_rate); + if (ret < 0) + return ret; + + break; + + default: + dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", + __func__, stub_clk->id); + break; + } + + pr_debug("%s: set rate=%ldkHz\n", __func__, new_rate); + return ret; +} + +static long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); + unsigned long new_rate = rate / 1000; /* kHz */ + + switch (stub_clk->id) { + case HI6220_STUB_ACPU0: + new_rate = hi6220_acpu_round_freq(stub_clk, new_rate); + + /* convert from kHz to Hz */ + new_rate *= 1000; + break; + + default: + dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", + __func__, stub_clk->id); + break; + } + + return new_rate; +} + +static const struct clk_ops hi6220_stub_clk_ops = { + .recalc_rate = hi6220_stub_clk_recalc_rate, + .round_rate = hi6220_stub_clk_round_rate, + .set_rate = hi6220_stub_clk_set_rate, +}; + +static int hi6220_stub_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_init_data init; + struct hi6220_stub_clk *stub_clk; + struct clk *clk; + struct device_node *np = pdev->dev.of_node; + int ret; + + stub_clk = devm_kzalloc(dev, sizeof(*stub_clk), GFP_KERNEL); + if (!stub_clk) + return -ENOMEM; + + stub_clk->dfs_map = syscon_regmap_lookup_by_phandle(np, + "hisilicon,hi6220-clk-sram"); + if (IS_ERR(stub_clk->dfs_map)) { + dev_err(dev, "failed to get sram regmap\n"); + return PTR_ERR(stub_clk->dfs_map); + } + + stub_clk->hw.init = &init; + stub_clk->dev = dev; + stub_clk->id = HI6220_STUB_ACPU0; + + /* Use mailbox client with blocking mode */ + stub_clk->cl.dev = dev; + stub_clk->cl.tx_done = NULL; + stub_clk->cl.tx_block = true; + stub_clk->cl.tx_tout = 500; + stub_clk->cl.knows_txdone = false; + + /* Allocate mailbox channel */ + stub_clk->mbox = mbox_request_channel(&stub_clk->cl, 0); + if (IS_ERR(stub_clk->mbox)) { + dev_err(dev, "failed get mailbox channel\n"); + return PTR_ERR(stub_clk->mbox); + } + + init.name = "acpu0"; + init.ops = &hi6220_stub_clk_ops; + init.num_parents = 0; + init.flags = 0; + + clk = devm_clk_register(dev, &stub_clk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (ret) { + dev_err(dev, "failed to register OF clock provider\n"); + return ret; + } + + /* initialize buffer to zero */ + regmap_write(stub_clk->dfs_map, ACPU_DFS_FLAG, 0x0); + regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, 0x0); + regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, 0x0); + + dev_dbg(dev, "Registered clock '%s'\n", init.name); + return 0; +} + +static const struct of_device_id hi6220_stub_clk_of_match[] = { + { .compatible = "hisilicon,hi6220-stub-clk", }, + {} +}; + +static struct platform_driver hi6220_stub_clk_driver = { + .driver = { + .name = "hi6220-stub-clk", + .of_match_table = hi6220_stub_clk_of_match, + }, + .probe = hi6220_stub_clk_probe, +}; + +static int __init hi6220_stub_clk_init(void) +{ + return platform_driver_register(&hi6220_stub_clk_driver); +} +subsys_initcall(hi6220_stub_clk_init); diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c new file mode 100644 index 000000000..a87809d4b --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -0,0 +1,309 @@ +/* + * Hisilicon Hi6220 clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang <bintian.wang@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/hi6220-clock.h> + +#include "clk.h" + + +/* clocks in AO (always on) controller */ +static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { + { HI6220_REF32K, "ref32k", NULL, 0, 32764, }, + { HI6220_CLK_TCXO, "clk_tcxo", NULL, 0, 19200000, }, + { HI6220_MMC1_PAD, "mmc1_pad", NULL, 0, 100000000, }, + { HI6220_MMC2_PAD, "mmc2_pad", NULL, 0, 100000000, }, + { HI6220_MMC0_PAD, "mmc0_pad", NULL, 0, 200000000, }, + { HI6220_PLL_BBP, "bbppll0", NULL, 0, 245760000, }, + { HI6220_PLL_GPU, "gpupll", NULL, 0, 1000000000,}, + { HI6220_PLL1_DDR, "ddrpll1", NULL, 0, 1066000000,}, + { HI6220_PLL_SYS, "syspll", NULL, 0, 1190400000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, 0, 1190400000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, 0, 1200000000,}, + { HI6220_PLL_MEDIA, "media_pll", NULL, 0, 1440000000,}, + { HI6220_PLL_DDR, "ddrpll0", NULL, 0, 1600000000,}, +}; + +static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { + { HI6220_300M, "clk_300m", "syspll", 1, 4, 0, }, + { HI6220_150M, "clk_150m", "clk_300m", 1, 2, 0, }, + { HI6220_PICOPHY_SRC, "picophy_src", "clk_150m", 1, 4, 0, }, + { HI6220_MMC0_SRC_SEL, "mmc0srcsel", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SRC_SEL, "mmc1srcsel", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SRC_SEL, "mmc2srcsel", "mmc2_sel", 1, 8, 0, }, + { HI6220_VPU_CODEC, "vpucodec", "codec_jpeg_aclk", 1, 2, 0, }, + { HI6220_MMC0_SMP, "mmc0_sample", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SMP, "mmc1_sample", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SMP, "mmc2_sample", "mmc2_sel", 1, 8, 0, }, +}; + +static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { + { HI6220_WDT0_PCLK, "wdt0_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, + { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, + { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, + { HI6220_TIMER3_PCLK, "timer3_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 18, 0, }, + { HI6220_TIMER4_PCLK, "timer4_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 19, 0, }, + { HI6220_TIMER5_PCLK, "timer5_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 20, 0, }, + { HI6220_TIMER6_PCLK, "timer6_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 21, 0, }, + { HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, }, + { HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, }, + { HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, }, + { HI6220_RTC0_PCLK, "rtc0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 25, 0, }, + { HI6220_RTC1_PCLK, "rtc1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 26, 0, }, +}; + +static void __init hi6220_clk_ao_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data_ao; + + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); + if (!clk_data_ao) + return; + + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, + ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); + + hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks, + ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao); + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, + ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); +} +CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); + + +/* clocks in sysctrl */ +static const char *mmc0_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc0_mux1_p[] __initdata = { "mmc0_mux0", "pll_media_gate", }; +static const char *mmc0_src_p[] __initdata = { "mmc0srcsel", "mmc0_div", }; +static const char *mmc1_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc1_mux1_p[] __initdata = { "mmc1_mux0", "pll_media_gate", }; +static const char *mmc1_src_p[] __initdata = { "mmc1srcsel", "mmc1_div", }; +static const char *mmc2_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc2_mux1_p[] __initdata = { "mmc2_mux0", "pll_media_gate", }; +static const char *mmc2_src_p[] __initdata = { "mmc2srcsel", "mmc2_div", }; +static const char *mmc0_sample_in[] __initdata = { "mmc0_sample", "mmc0_pad", }; +static const char *mmc1_sample_in[] __initdata = { "mmc1_sample", "mmc1_pad", }; +static const char *mmc2_sample_in[] __initdata = { "mmc2_sample", "mmc2_pad", }; +static const char *uart1_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart2_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart3_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart4_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *hifi_src[] __initdata = { "syspll", "pll_media_gate", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { + { HI6220_MMC0_CLK, "mmc0_clk", "mmc0_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC0_CIUCLK, "mmc0_ciuclk", "mmc0_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC1_CLK, "mmc1_clk", "mmc1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC1_CIUCLK, "mmc1_ciuclk", "mmc1_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC2_CLK, "mmc2_clk", "mmc2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_MMC2_CIUCLK, "mmc2_ciuclk", "mmc2_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_USBOTG_HCLK, "usbotg_hclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 4, 0, }, + { HI6220_CLK_PICOPHY, "clk_picophy", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 5, 0, }, + { HI6220_HIFI, "hifi_clk", "hifi_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 0, 0, }, + { HI6220_DACODEC_PCLK, "dacodec_pclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 5, 0, }, + { HI6220_EDMAC_ACLK, "edmac_aclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x220, 2, 0, }, + { HI6220_CS_ATB, "cs_atb", "cs_atb_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 0, 0, }, + { HI6220_I2C0_CLK, "i2c0_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 1, 0, }, + { HI6220_I2C1_CLK, "i2c1_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 2, 0, }, + { HI6220_I2C2_CLK, "i2c2_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 3, 0, }, + { HI6220_I2C3_CLK, "i2c3_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 4, 0, }, + { HI6220_UART1_PCLK, "uart1_pclk", "uart1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 5, 0, }, + { HI6220_UART2_PCLK, "uart2_pclk", "uart2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 6, 0, }, + { HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, }, + { HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, }, + { HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, }, + { HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, }, + { HI6220_DAPB_CLK, "dapb_clk", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IS_CRITICAL, 0x230, 18, 0, }, + { HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, }, + { HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, }, + { HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, }, + { HI6220_MMC1_SYSPLL, "mmc1_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 2, 0, }, + { HI6220_MMC2_SYSPLL, "mmc2_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 3, 0, }, + { HI6220_MMC0_SEL, "mmc0_sel", "mmc0_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 6, 0, }, + { HI6220_MMC1_SEL, "mmc1_sel", "mmc1_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 7, 0, }, + { HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, }, + { HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, }, + { HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, }, + { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IS_CRITICAL, 0x270, 12, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = { + { HI6220_MMC0_SRC, "mmc0_src", mmc0_src_p, ARRAY_SIZE(mmc0_src_p), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC0_SMP_IN, "mmc0_smp_in", mmc0_sample_in, ARRAY_SIZE(mmc0_sample_in), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC1_SRC, "mmc1_src", mmc1_src_p, ARRAY_SIZE(mmc1_src_p), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC1_SMP_IN, "mmc1_smp_in", mmc1_sample_in, ARRAY_SIZE(mmc1_sample_in), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC2_SRC, "mmc2_src", mmc2_src_p, ARRAY_SIZE(mmc2_src_p), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_MMC2_SMP_IN, "mmc2_smp_in", mmc2_sample_in, ARRAY_SIZE(mmc2_sample_in), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_HIFI_SRC, "hifi_src", hifi_src, ARRAY_SIZE(hifi_src), CLK_SET_RATE_PARENT, 0x400, 0, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART1_SRC, "uart1_src", uart1_src, ARRAY_SIZE(uart1_src), CLK_SET_RATE_PARENT, 0x400, 1, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART2_SRC, "uart2_src", uart2_src, ARRAY_SIZE(uart2_src), CLK_SET_RATE_PARENT, 0x400, 2, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART3_SRC, "uart3_src", uart3_src, ARRAY_SIZE(uart3_src), CLK_SET_RATE_PARENT, 0x400, 3, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART4_SRC, "uart4_src", uart4_src, ARRAY_SIZE(uart4_src), CLK_SET_RATE_PARENT, 0x400, 4, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX0, "mmc0_mux0", mmc0_mux0_p, ARRAY_SIZE(mmc0_mux0_p), CLK_SET_RATE_PARENT, 0x400, 5, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX0, "mmc1_mux0", mmc1_mux0_p, ARRAY_SIZE(mmc1_mux0_p), CLK_SET_RATE_PARENT, 0x400, 11, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX0, "mmc2_mux0", mmc2_mux0_p, ARRAY_SIZE(mmc2_mux0_p), CLK_SET_RATE_PARENT, 0x400, 12, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX1, "mmc0_mux1", mmc0_mux1_p, ARRAY_SIZE(mmc0_mux1_p), CLK_SET_RATE_PARENT, 0x400, 13, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX1, "mmc1_mux1", mmc1_mux1_p, ARRAY_SIZE(mmc1_mux1_p), CLK_SET_RATE_PARENT, 0x400, 14, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX1, "mmc2_mux1", mmc2_mux1_p, ARRAY_SIZE(mmc2_mux1_p), CLK_SET_RATE_PARENT, 0x400, 15, 1, CLK_MUX_HIWORD_MASK,}, +}; + +static struct hi6220_divider_clock hi6220_div_clks_sys[] __initdata = { + { HI6220_CLK_BUS, "clk_bus", "clk_300m", CLK_SET_RATE_PARENT, 0x490, 0, 4, 7, }, + { HI6220_MMC0_DIV, "mmc0_div", "mmc0_syspll", CLK_SET_RATE_PARENT, 0x494, 0, 6, 7, }, + { HI6220_MMC1_DIV, "mmc1_div", "mmc1_syspll", CLK_SET_RATE_PARENT, 0x498, 0, 6, 7, }, + { HI6220_MMC2_DIV, "mmc2_div", "mmc2_syspll", CLK_SET_RATE_PARENT, 0x49c, 0, 6, 7, }, + { HI6220_HIFI_DIV, "hifi_div", "hifi_sel", CLK_SET_RATE_PARENT, 0x4a0, 0, 4, 7, }, + { HI6220_BBPPLL0_DIV, "bbppll0_div", "bbppll_sel", CLK_SET_RATE_PARENT, 0x4a0, 8, 6, 15,}, + { HI6220_CS_DAPB, "cs_dapb", "picophy_src", CLK_SET_RATE_PARENT, 0x4a0, 24, 2, 31,}, + { HI6220_CS_ATB_DIV, "cs_atb_div", "cs_atb_syspll", CLK_SET_RATE_PARENT, 0x4a4, 0, 4, 7, }, +}; + +static void __init hi6220_clk_sys_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_SYS_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_sys, + ARRAY_SIZE(hi6220_separated_gate_clks_sys), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_sys, + ARRAY_SIZE(hi6220_mux_clks_sys), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_sys, + ARRAY_SIZE(hi6220_div_clks_sys), clk_data); +} +CLK_OF_DECLARE_DRIVER(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); + + +/* clocks in media controller */ +static const char *clk_1000_1200_src[] __initdata = { "pll_gpu_gate", "media_syspll_src", }; +static const char *clk_1440_1200_src[] __initdata = { "media_syspll_src", "media_pll_src", }; +static const char *clk_1000_1440_src[] __initdata = { "pll_gpu_gate", "media_pll_src", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_media[] __initdata = { + { HI6220_DSI_PCLK, "dsi_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 0, 0, }, + { HI6220_G3D_PCLK, "g3d_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 1, 0, }, + { HI6220_ACLK_CODEC_VPU, "aclk_codec_vpu", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 3, 0, }, + { HI6220_ISP_SCLK, "isp_sclk", "isp_sclk_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 5, 0, }, + { HI6220_ADE_CORE, "ade_core", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 6, 0, }, + { HI6220_MED_MMU, "media_mmu", "mmu_clk", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 8, 0, }, + { HI6220_CFG_CSI4PHY, "cfg_csi4phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 9, 0, }, + { HI6220_CFG_CSI2PHY, "cfg_csi2phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 10, 0, }, + { HI6220_ISP_SCLK_GATE, "isp_sclk_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 11, 0, }, + { HI6220_ISP_SCLK_GATE1, "isp_sclk_gate1", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 12, 0, }, + { HI6220_ADE_CORE_GATE, "ade_core_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 14, 0, }, + { HI6220_CODEC_VPU_GATE, "codec_vpu_gate", "clk_1000_1440", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 15, 0, }, + { HI6220_MED_SYSPLL, "media_syspll_src", "media_syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 17, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_media[] __initdata = { + { HI6220_1440_1200, "clk_1440_1200", clk_1440_1200_src, ARRAY_SIZE(clk_1440_1200_src), CLK_SET_RATE_PARENT, 0x51c, 0, 1, 0, }, + { HI6220_1000_1200, "clk_1000_1200", clk_1000_1200_src, ARRAY_SIZE(clk_1000_1200_src), CLK_SET_RATE_PARENT, 0x51c, 1, 1, 0, }, + { HI6220_1000_1440, "clk_1000_1440", clk_1000_1440_src, ARRAY_SIZE(clk_1000_1440_src), CLK_SET_RATE_PARENT, 0x51c, 6, 1, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_media[] __initdata = { + { HI6220_CODEC_JPEG, "codec_jpeg_aclk", "media_pll_src", CLK_SET_RATE_PARENT, 0xcbc, 0, 4, 23, }, + { HI6220_ISP_SCLK_SRC, "isp_sclk_src", "isp_sclk_gate", CLK_SET_RATE_PARENT, 0xcbc, 8, 4, 15, }, + { HI6220_ISP_SCLK1, "isp_sclk1", "isp_sclk_gate1", CLK_SET_RATE_PARENT, 0xcbc, 24, 4, 31, }, + { HI6220_ADE_CORE_SRC, "ade_core_src", "ade_core_gate", CLK_SET_RATE_PARENT, 0xcc0, 16, 3, 23, }, + { HI6220_ADE_PIX_SRC, "ade_pix_src", "clk_1440_1200", CLK_SET_RATE_PARENT, 0xcc0, 24, 6, 31, }, + { HI6220_G3D_CLK, "g3d_clk", "clk_1000_1200", CLK_SET_RATE_PARENT, 0xcc4, 8, 4, 15, }, + { HI6220_CODEC_VPU_SRC, "codec_vpu_src", "codec_vpu_gate", CLK_SET_RATE_PARENT, 0xcc4, 24, 6, 31, }, +}; + +static void __init hi6220_clk_media_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_MEDIA_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_media, + ARRAY_SIZE(hi6220_separated_gate_clks_media), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_media, + ARRAY_SIZE(hi6220_mux_clks_media), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_media, + ARRAY_SIZE(hi6220_div_clks_media), clk_data); +} +CLK_OF_DECLARE_DRIVER(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init); + + +/* clocks in pmctrl */ +static struct hisi_gate_clock hi6220_gate_clks_power[] __initdata = { + { HI6220_PLL_GPU_GATE, "pll_gpu_gate", "gpupll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x8, 0, 0, }, + { HI6220_PLL1_DDR_GATE, "pll1_ddr_gate", "ddrpll1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x10, 0, 0, }, + { HI6220_PLL_DDR_GATE, "pll_ddr_gate", "ddrpll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x18, 0, 0, }, + { HI6220_PLL_MEDIA_GATE, "pll_media_gate", "media_pll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x38, 0, 0, }, + { HI6220_PLL0_BBP_GATE, "pll0_bbp_gate", "bbppll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x48, 0, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_power[] __initdata = { + { HI6220_DDRC_SRC, "ddrc_src", "ddr_sel_src", CLK_SET_RATE_PARENT, 0x5a8, 0, 4, 0, }, + { HI6220_DDRC_AXI1, "ddrc_axi1", "ddrc_src", CLK_SET_RATE_PARENT, 0x5a8, 8, 2, 0, }, +}; + +static void __init hi6220_clk_power_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_POWER_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate(hi6220_gate_clks_power, + ARRAY_SIZE(hi6220_gate_clks_power), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_power, + ARRAY_SIZE(hi6220_div_clks_power), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init); + +/* clocks in acpu */ +static const struct hisi_gate_clock hi6220_acpu_sc_gate_sep_clks[] = { + { HI6220_ACPU_SFT_AT_S, "sft_at_s", "cs_atb", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0xc, 11, 0, }, +}; + +static void __init hi6220_clk_acpu_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi6220_acpu_sc_gate_sep_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_acpu_sc_gate_sep_clks, + ARRAY_SIZE(hi6220_acpu_sc_gate_sep_clks), + clk_data); +} + +CLK_OF_DECLARE(hi6220_clk_acpu, "hisilicon,hi6220-acpu-sctrl", hi6220_clk_acpu_init); diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c new file mode 100644 index 000000000..b38e03da1 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hip04.c @@ -0,0 +1,56 @@ +/* + * Hisilicon HiP04 clock driver + * + * Copyright (c) 2013-2014 Hisilicon Limited. + * Copyright (c) 2013-2014 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/hip04-clock.h> + +#include "clk.h" + +/* fixed rate clocks */ +static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = { + { HIP04_OSC50M, "osc50m", NULL, 0, 50000000, }, + { HIP04_CLK_50M, "clk50m", NULL, 0, 50000000, }, + { HIP04_CLK_168M, "clk168m", NULL, 0, 168750000, }, +}; + +static void __init hip04_clk_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HIP04_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hip04_fixed_rate_clks, + ARRAY_SIZE(hip04_fixed_rate_clks), + clk_data); +} +CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init); diff --git a/drivers/clk/hisilicon/clk-hisi-phase.c b/drivers/clk/hisilicon/clk-hisi-phase.c new file mode 100644 index 000000000..5bce9297b --- /dev/null +++ b/drivers/clk/hisilicon/clk-hisi-phase.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 HiSilicon Technologies Co., Ltd. + * + * Simple HiSilicon phase clock implementation. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "clk.h" + +struct clk_hisi_phase { + struct clk_hw hw; + void __iomem *reg; + u32 *phase_degrees; + u32 *phase_regvals; + u8 phase_num; + u32 mask; + u8 shift; + u8 flags; + spinlock_t *lock; +}; + +#define to_clk_hisi_phase(_hw) container_of(_hw, struct clk_hisi_phase, hw) + +static int hisi_phase_regval_to_degrees(struct clk_hisi_phase *phase, + u32 regval) +{ + int i; + + for (i = 0; i < phase->phase_num; i++) + if (phase->phase_regvals[i] == regval) + return phase->phase_degrees[i]; + + return -EINVAL; +} + +static int hisi_clk_get_phase(struct clk_hw *hw) +{ + struct clk_hisi_phase *phase = to_clk_hisi_phase(hw); + u32 regval; + + regval = readl(phase->reg); + regval = (regval & phase->mask) >> phase->shift; + + return hisi_phase_regval_to_degrees(phase, regval); +} + +static int hisi_phase_degrees_to_regval(struct clk_hisi_phase *phase, + int degrees) +{ + int i; + + for (i = 0; i < phase->phase_num; i++) + if (phase->phase_degrees[i] == degrees) + return phase->phase_regvals[i]; + + return -EINVAL; +} + +static int hisi_clk_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_hisi_phase *phase = to_clk_hisi_phase(hw); + unsigned long flags = 0; + int regval; + u32 val; + + regval = hisi_phase_degrees_to_regval(phase, degrees); + if (regval < 0) + return regval; + + spin_lock_irqsave(phase->lock, flags); + + val = clk_readl(phase->reg); + val &= ~phase->mask; + val |= regval << phase->shift; + clk_writel(val, phase->reg); + + spin_unlock_irqrestore(phase->lock, flags); + + return 0; +} + +static const struct clk_ops clk_phase_ops = { + .get_phase = hisi_clk_get_phase, + .set_phase = hisi_clk_set_phase, +}; + +struct clk *clk_register_hisi_phase(struct device *dev, + const struct hisi_phase_clock *clks, + void __iomem *base, spinlock_t *lock) +{ + struct clk_hisi_phase *phase; + struct clk_init_data init; + + phase = devm_kzalloc(dev, sizeof(struct clk_hisi_phase), GFP_KERNEL); + if (!phase) + return ERR_PTR(-ENOMEM); + + init.name = clks->name; + init.ops = &clk_phase_ops; + init.flags = clks->flags | CLK_IS_BASIC; + init.parent_names = clks->parent_names ? &clks->parent_names : NULL; + init.num_parents = clks->parent_names ? 1 : 0; + + phase->reg = base + clks->offset; + phase->shift = clks->shift; + phase->mask = (BIT(clks->width) - 1) << clks->shift; + phase->lock = lock; + phase->phase_degrees = clks->phase_degrees; + phase->phase_regvals = clks->phase_regvals; + phase->phase_num = clks->phase_num; + phase->hw.init = &init; + + return devm_clk_register(dev, &phase->hw); +} +EXPORT_SYMBOL_GPL(clk_register_hisi_phase); diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c new file mode 100644 index 000000000..9584f0c32 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/of_address.h> +#include <dt-bindings/clock/hix5hd2-clock.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "clk.h" + +static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { + { HIX5HD2_FIXED_1200M, "1200m", NULL, 0, 1200000000, }, + { HIX5HD2_FIXED_400M, "400m", NULL, 0, 400000000, }, + { HIX5HD2_FIXED_48M, "48m", NULL, 0, 48000000, }, + { HIX5HD2_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HIX5HD2_FIXED_600M, "600m", NULL, 0, 600000000, }, + { HIX5HD2_FIXED_300M, "300m", NULL, 0, 300000000, }, + { HIX5HD2_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HIX5HD2_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HIX5HD2_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HIX5HD2_FIXED_40M, "40m", NULL, 0, 40000000, }, + { HIX5HD2_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HIX5HD2_FIXED_1728M, "1728m", NULL, 0, 1728000000, }, + { HIX5HD2_FIXED_28P8M, "28p8m", NULL, 0, 28000000, }, + { HIX5HD2_FIXED_432M, "432m", NULL, 0, 432000000, }, + { HIX5HD2_FIXED_345P6M, "345p6m", NULL, 0, 345000000, }, + { HIX5HD2_FIXED_288M, "288m", NULL, 0, 288000000, }, + { HIX5HD2_FIXED_60M, "60m", NULL, 0, 60000000, }, + { HIX5HD2_FIXED_750M, "750m", NULL, 0, 750000000, }, + { HIX5HD2_FIXED_500M, "500m", NULL, 0, 500000000, }, + { HIX5HD2_FIXED_54M, "54m", NULL, 0, 54000000, }, + { HIX5HD2_FIXED_27M, "27m", NULL, 0, 27000000, }, + { HIX5HD2_FIXED_1500M, "1500m", NULL, 0, 1500000000, }, + { HIX5HD2_FIXED_375M, "375m", NULL, 0, 375000000, }, + { HIX5HD2_FIXED_187M, "187m", NULL, 0, 187000000, }, + { HIX5HD2_FIXED_250M, "250m", NULL, 0, 250000000, }, + { HIX5HD2_FIXED_125M, "125m", NULL, 0, 125000000, }, + { HIX5HD2_FIXED_2P02M, "2m", NULL, 0, 2000000, }, + { HIX5HD2_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HIX5HD2_FIXED_25M, "25m", NULL, 0, 25000000, }, + { HIX5HD2_FIXED_83M, "83m", NULL, 0, 83333333, }, +}; + +static const char *const sfc_mux_p[] __initconst = { + "24m", "150m", "200m", "100m", "75m", }; +static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; + +static const char *const sdio_mux_p[] __initconst = { + "75m", "100m", "50m", "15m", }; +static u32 sdio_mux_table[] = {0, 1, 2, 3}; + +static const char *const fephy_mux_p[] __initconst = { "25m", "125m"}; +static u32 fephy_mux_table[] = {0, 1}; + + +static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = { + { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p), + CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, }, + { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, }, + { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, }, + { HIX5HD2_FEPHY_MUX, "fephy_mux", + fephy_mux_p, ARRAY_SIZE(fephy_mux_p), + CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, }, +}; + +static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = { + /* sfc */ + { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux", + CLK_SET_RATE_PARENT, 0x5c, 0, 0, }, + { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc", + CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, }, + /* sdio0 */ + { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m", + CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, + { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux", + CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, + { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu", + CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, }, + /* sdio1 */ + { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m", + CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, + { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, + { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu", + CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, }, + /* gsf */ + { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, }, + { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, }, + { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys", + CLK_SET_RATE_PARENT, 0x120, 0, 0, }, + /* wdg0 */ + { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m", + CLK_SET_RATE_PARENT, 0x178, 0, 0, }, + { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0", + CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, }, + /* I2C */ + {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m", + CLK_SET_RATE_PARENT, 0x06c, 4, 0, }, + {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0", + CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m", + CLK_SET_RATE_PARENT, 0x06c, 8, 0, }, + {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1", + CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m", + CLK_SET_RATE_PARENT, 0x06c, 12, 0, }, + {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2", + CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m", + CLK_SET_RATE_PARENT, 0x06c, 16, 0, }, + {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3", + CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m", + CLK_SET_RATE_PARENT, 0x06c, 20, 0, }, + {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4", + CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m", + CLK_SET_RATE_PARENT, 0x06c, 0, 0, }, + {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5", + CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, }, +}; + +enum hix5hd2_clk_type { + TYPE_COMPLEX, + TYPE_ETHER, +}; + +struct hix5hd2_complex_clock { + const char *name; + const char *parent_name; + u32 id; + u32 ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + u32 phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; + enum hix5hd2_clk_type type; +}; + +struct hix5hd2_clk_complex { + struct clk_hw hw; + u32 id; + void __iomem *ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + void __iomem *phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; +}; + +static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = { + {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK, + 0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER}, + {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK, + 0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER}, + {"clk_sata", NULL, HIX5HD2_SATA_CLK, + 0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX}, + {"clk_usb", NULL, HIX5HD2_USB_CLK, + 0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX}, +}; + +#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw) + +static int clk_ether_prepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask; + writel_relaxed(val, clk->ctrl_reg); + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val &= ~(clk->phy_clk_mask); + val |= clk->phy_rst_mask; + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(30); + return 0; +} + +static void clk_ether_unprepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); +} + +static const struct clk_ops clk_ether_ops = { + .prepare = clk_ether_prepare, + .unprepare = clk_ether_unprepare, +}; + +static int clk_complex_enable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask; + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + + return 0; +} + +static void clk_complex_disable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_rst_mask; + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_rst_mask; + val &= ~(clk->phy_clk_mask); + writel_relaxed(val, clk->phy_reg); +} + +static const struct clk_ops clk_complex_ops = { + .enable = clk_complex_enable, + .disable = clk_complex_disable, +}; + +static void __init +hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, int nums, + struct hisi_clock_data *data) +{ + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + struct hix5hd2_clk_complex *p_clk; + struct clk *clk; + struct clk_init_data init; + + p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); + if (!p_clk) + return; + + init.name = clks[i].name; + if (clks[i].type == TYPE_ETHER) + init.ops = &clk_ether_ops; + else + init.ops = &clk_complex_ops; + + init.flags = CLK_IS_BASIC; + init.parent_names = + (clks[i].parent_name ? &clks[i].parent_name : NULL); + init.num_parents = (clks[i].parent_name ? 1 : 0); + + p_clk->ctrl_reg = base + clks[i].ctrl_reg; + p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask; + p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask; + p_clk->phy_reg = base + clks[i].phy_reg; + p_clk->phy_clk_mask = clks[i].phy_clk_mask; + p_clk->phy_rst_mask = clks[i].phy_rst_mask; + p_clk->hw.init = &init; + + clk = clk_register(NULL, &p_clk->hw); + if (IS_ERR(clk)) { + kfree(p_clk); + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + data->clk_data.clks[clks[i].id] = clk; + } +} + +static void __init hix5hd2_clk_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HIX5HD2_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hix5hd2_fixed_rate_clks, + ARRAY_SIZE(hix5hd2_fixed_rate_clks), + clk_data); + hisi_clk_register_mux(hix5hd2_mux_clks, ARRAY_SIZE(hix5hd2_mux_clks), + clk_data); + hisi_clk_register_gate(hix5hd2_gate_clks, + ARRAY_SIZE(hix5hd2_gate_clks), clk_data); + hix5hd2_clk_register_complex(hix5hd2_complex_clks, + ARRAY_SIZE(hix5hd2_complex_clks), + clk_data); +} + +CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c new file mode 100644 index 000000000..953c8dace --- /dev/null +++ b/drivers/clk/hisilicon/clk.c @@ -0,0 +1,357 @@ +/* + * Hisilicon clock driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * Xin Li <li.xin@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include "clk.h" + +static DEFINE_SPINLOCK(hisi_clk_lock); + +struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev, + int nr_clks) +{ + struct hisi_clock_data *clk_data; + struct resource *res; + struct clk **clk_table; + + clk_data = devm_kmalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return NULL; + clk_data->base = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!clk_data->base) + return NULL; + + clk_table = devm_kmalloc_array(&pdev->dev, nr_clks, + sizeof(*clk_table), + GFP_KERNEL); + if (!clk_table) + return NULL; + + clk_data->clk_data.clks = clk_table; + clk_data->clk_data.clk_num = nr_clks; + + return clk_data; +} +EXPORT_SYMBOL_GPL(hisi_clk_alloc); + +struct hisi_clock_data *hisi_clk_init(struct device_node *np, + int nr_clks) +{ + struct hisi_clock_data *clk_data; + struct clk **clk_table; + void __iomem *base; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: failed to map clock registers\n", __func__); + goto err; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err; + + clk_data->base = base; + clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL); + if (!clk_table) + goto err_data; + + clk_data->clk_data.clks = clk_table; + clk_data->clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data); + return clk_data; +err_data: + kfree(clk_data); +err: + return NULL; +} +EXPORT_SYMBOL_GPL(hisi_clk_init); + +int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_fixed_rate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + clks[i].fixed_rate); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err; + } + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; + +err: + while (i--) + clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]); + + return PTR_ERR(clk); +} +EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate); + +int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks, + int nums, + struct hisi_clock_data *data) +{ + struct clk *clk; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_fixed_factor(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, clks[i].mult, + clks[i].div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err; + } + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; + +err: + while (i--) + clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]); + + return PTR_ERR(clk); +} +EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor); + +int hisi_clk_register_mux(const struct hisi_mux_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + u32 mask = BIT(clks[i].width) - 1; + + clk = clk_register_mux_table(NULL, clks[i].name, + clks[i].parent_names, + clks[i].num_parents, clks[i].flags, + base + clks[i].offset, clks[i].shift, + mask, clks[i].mux_flags, + clks[i].table, &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; + +err: + while (i--) + clk_unregister_mux(data->clk_data.clks[clks[i].id]); + + return PTR_ERR(clk); +} +EXPORT_SYMBOL_GPL(hisi_clk_register_mux); + +int hisi_clk_register_phase(struct device *dev, + const struct hisi_phase_clock *clks, + int nums, struct hisi_clock_data *data) +{ + void __iomem *base = data->base; + struct clk *clk; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_hisi_phase(dev, &clks[i], base, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + clks[i].name); + return PTR_ERR(clk); + } + + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hisi_clk_register_phase); + +int hisi_clk_register_divider(const struct hisi_divider_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_divider_table(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, clks[i].width, + clks[i].div_flags, + clks[i].table, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; + +err: + while (i--) + clk_unregister_divider(data->clk_data.clks[clks[i].id]); + + return PTR_ERR(clk); +} +EXPORT_SYMBOL_GPL(hisi_clk_register_divider); + +int hisi_clk_register_gate(const struct hisi_gate_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_gate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].bit_idx, + clks[i].gate_flags, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } + + return 0; + +err: + while (i--) + clk_unregister_gate(data->clk_data.clks[clks[i].id]); + + return PTR_ERR(clk); +} +EXPORT_SYMBOL_GPL(hisi_clk_register_gate); + +void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = hisi_register_clkgate_sep(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].bit_idx, + clks[i].gate_flags, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} +EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); + +void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = hi6220_register_clkdiv(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].mask_bit, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h new file mode 100644 index 000000000..8d7ee5c32 --- /dev/null +++ b/drivers/clk/hisilicon/clk.h @@ -0,0 +1,171 @@ +/* + * Hisilicon Hi3620 clock gate driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * Xin Li <li.xin@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __HISI_CLK_H +#define __HISI_CLK_H + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/spinlock.h> + +struct platform_device; + +struct hisi_clock_data { + struct clk_onecell_data clk_data; + void __iomem *base; +}; + +struct hisi_fixed_rate_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long fixed_rate; +}; + +struct hisi_fixed_factor_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long mult; + unsigned long div; + unsigned long flags; +}; + +struct hisi_mux_clock { + unsigned int id; + const char *name; + const char *const *parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; + u32 *table; + const char *alias; +}; + +struct hisi_phase_clock { + unsigned int id; + const char *name; + const char *parent_names; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u32 *phase_degrees; + u32 *phase_regvals; + u8 phase_num; +}; + +struct hisi_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + struct clk_div_table *table; + const char *alias; +}; + +struct hi6220_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u32 mask_bit; + const char *alias; +}; + +struct hisi_gate_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; + const char *alias; +}; + +struct clk *hisi_register_clkgate_sep(struct device *, const char *, + const char *, unsigned long, + void __iomem *, u8, + u8, spinlock_t *); +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock); + +struct hisi_clock_data *hisi_clk_alloc(struct platform_device *, int); +struct hisi_clock_data *hisi_clk_init(struct device_node *, int); +int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *, + int, struct hisi_clock_data *); +int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *, + int, struct hisi_clock_data *); +int hisi_clk_register_mux(const struct hisi_mux_clock *, int, + struct hisi_clock_data *); +struct clk *clk_register_hisi_phase(struct device *dev, + const struct hisi_phase_clock *clks, + void __iomem *base, spinlock_t *lock); +int hisi_clk_register_phase(struct device *dev, + const struct hisi_phase_clock *clks, + int nums, struct hisi_clock_data *data); +int hisi_clk_register_divider(const struct hisi_divider_clock *, + int, struct hisi_clock_data *); +int hisi_clk_register_gate(const struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate_sep(const struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hi6220_clk_register_divider(const struct hi6220_divider_clock *, + int, struct hisi_clock_data *); + +#define hisi_clk_unregister(type) \ +static inline \ +void hisi_clk_unregister_##type(const struct hisi_##type##_clock *clks, \ + int nums, struct hisi_clock_data *data) \ +{ \ + struct clk **clocks = data->clk_data.clks; \ + int i; \ + for (i = 0; i < nums; i++) { \ + int id = clks[i].id; \ + if (clocks[id]) \ + clk_unregister_##type(clocks[id]); \ + } \ +} + +hisi_clk_unregister(fixed_rate) +hisi_clk_unregister(fixed_factor) +hisi_clk_unregister(mux) +hisi_clk_unregister(divider) +hisi_clk_unregister(gate) + +#endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c new file mode 100644 index 000000000..9f46cf9dc --- /dev/null +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,158 @@ +/* + * Hisilicon hi6220 SoC divider clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang <bintian.wang@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/spinlock.h> + +#include "clk.h" + +#define div_mask(width) ((1 << (width)) - 1) + +/** + * struct hi6220_clk_divider - divider clock for hi6220 + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @mask: mask for setting divider rate + * @table: the div table that the divider supports + * @lock: register lock + */ +struct hi6220_clk_divider { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u32 mask; + const struct clk_div_table *table; + spinlock_t *lock; +}; + +#define to_hi6220_clk_divider(_hw) \ + container_of(_hw, struct hi6220_clk_divider, hw) + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + return divider_recalc_rate(hw, parent_rate, val, dclk->table, + CLK_DIVIDER_ROUND_CLOSEST, dclk->width); +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + return divider_round_rate(hw, rate, prate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + value = divider_get_val(rate, parent_rate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(div_mask(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mask; + + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static const struct clk_ops hi6220_clkdiv_ops = { + .recalc_rate = hi6220_clkdiv_recalc_rate, + .round_rate = hi6220_clkdiv_round_rate, + .set_rate = hi6220_clkdiv_set_rate, +}; + +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) +{ + struct hi6220_clk_divider *div; + struct clk *clk; + struct clk_init_data init; + struct clk_div_table *table; + u32 max_div, min_div; + int i; + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < max_div; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 1; + } + + init.name = name; + init.ops = &hi6220_clkdiv_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + /* struct hi6220_clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->mask = mask_bit ? BIT(mask_bit) : 0; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + clk = clk_register(dev, &div->hw); + if (IS_ERR(clk)) { + kfree(table); + kfree(div); + } + + return clk; +} diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c new file mode 100644 index 000000000..f36bdef91 --- /dev/null +++ b/drivers/clk/hisilicon/clkgate-separated.c @@ -0,0 +1,127 @@ +/* + * Hisilicon clock separated gate driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * Xin Li <li.xin@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "clk.h" + +/* clock separated gate register offset */ +#define CLKGATE_SEPERATED_ENABLE 0x0 +#define CLKGATE_SEPERATED_DISABLE 0x4 +#define CLKGATE_SEPERATED_STATUS 0x8 + +struct clkgate_separated { + struct clk_hw hw; + void __iomem *enable; /* enable register */ + u8 bit_idx; /* bits in enable/disable register */ + u8 flags; + spinlock_t *lock; +}; + +static int clkgate_separated_enable(struct clk_hw *hw) +{ + struct clkgate_separated *sclk; + unsigned long flags = 0; + u32 reg; + + sclk = container_of(hw, struct clkgate_separated, hw); + if (sclk->lock) + spin_lock_irqsave(sclk->lock, flags); + reg = BIT(sclk->bit_idx); + writel_relaxed(reg, sclk->enable); + readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + if (sclk->lock) + spin_unlock_irqrestore(sclk->lock, flags); + return 0; +} + +static void clkgate_separated_disable(struct clk_hw *hw) +{ + struct clkgate_separated *sclk; + unsigned long flags = 0; + u32 reg; + + sclk = container_of(hw, struct clkgate_separated, hw); + if (sclk->lock) + spin_lock_irqsave(sclk->lock, flags); + reg = BIT(sclk->bit_idx); + writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE); + readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + if (sclk->lock) + spin_unlock_irqrestore(sclk->lock, flags); +} + +static int clkgate_separated_is_enabled(struct clk_hw *hw) +{ + struct clkgate_separated *sclk; + u32 reg; + + sclk = container_of(hw, struct clkgate_separated, hw); + reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); + reg &= BIT(sclk->bit_idx); + + return reg ? 1 : 0; +} + +static const struct clk_ops clkgate_separated_ops = { + .enable = clkgate_separated_enable, + .disable = clkgate_separated_disable, + .is_enabled = clkgate_separated_is_enabled, +}; + +struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct clkgate_separated *sclk; + struct clk *clk; + struct clk_init_data init; + + sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); + if (!sclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clkgate_separated_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + sclk->enable = reg + CLKGATE_SEPERATED_ENABLE; + sclk->bit_idx = bit_idx; + sclk->flags = clk_gate_flags; + sclk->hw.init = &init; + sclk->lock = lock; + + clk = clk_register(dev, &sclk->hw); + if (IS_ERR(clk)) + kfree(sclk); + return clk; +} diff --git a/drivers/clk/hisilicon/crg-hi3516cv300.c b/drivers/clk/hisilicon/crg-hi3516cv300.c new file mode 100644 index 000000000..53450b651 --- /dev/null +++ b/drivers/clk/hisilicon/crg-hi3516cv300.c @@ -0,0 +1,330 @@ +/* + * Hi3516CV300 Clock and Reset Generator Driver + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/hi3516cv300-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "crg.h" +#include "reset.h" + +/* hi3516CV300 core CRG */ +#define HI3516CV300_INNER_CLK_OFFSET 64 +#define HI3516CV300_FIXED_3M 65 +#define HI3516CV300_FIXED_6M 66 +#define HI3516CV300_FIXED_24M 67 +#define HI3516CV300_FIXED_49P5 68 +#define HI3516CV300_FIXED_50M 69 +#define HI3516CV300_FIXED_83P3M 70 +#define HI3516CV300_FIXED_99M 71 +#define HI3516CV300_FIXED_100M 72 +#define HI3516CV300_FIXED_148P5M 73 +#define HI3516CV300_FIXED_198M 74 +#define HI3516CV300_FIXED_297M 75 +#define HI3516CV300_UART_MUX 76 +#define HI3516CV300_FMC_MUX 77 +#define HI3516CV300_MMC0_MUX 78 +#define HI3516CV300_MMC1_MUX 79 +#define HI3516CV300_MMC2_MUX 80 +#define HI3516CV300_MMC3_MUX 81 +#define HI3516CV300_PWM_MUX 82 +#define HI3516CV300_CRG_NR_CLKS 128 + +static const struct hisi_fixed_rate_clock hi3516cv300_fixed_rate_clks[] = { + { HI3516CV300_FIXED_3M, "3m", NULL, 0, 3000000, }, + { HI3516CV300_FIXED_6M, "6m", NULL, 0, 6000000, }, + { HI3516CV300_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3516CV300_FIXED_49P5, "49.5m", NULL, 0, 49500000, }, + { HI3516CV300_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3516CV300_FIXED_83P3M, "83.3m", NULL, 0, 83300000, }, + { HI3516CV300_FIXED_99M, "99m", NULL, 0, 99000000, }, + { HI3516CV300_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HI3516CV300_FIXED_148P5M, "148.5m", NULL, 0, 148500000, }, + { HI3516CV300_FIXED_198M, "198m", NULL, 0, 198000000, }, + { HI3516CV300_FIXED_297M, "297m", NULL, 0, 297000000, }, + { HI3516CV300_APB_CLK, "apb", NULL, 0, 50000000, }, +}; + +static const char *const uart_mux_p[] = {"24m", "6m"}; +static const char *const fmc_mux_p[] = { + "24m", "83.3m", "148.5m", "198m", "297m" +}; +static const char *const mmc_mux_p[] = {"49.5m"}; +static const char *const mmc2_mux_p[] = {"99m", "49.5m"}; +static const char *const pwm_mux_p[] = {"3m", "50m", "24m", "24m"}; + +static u32 uart_mux_table[] = {0, 1}; +static u32 fmc_mux_table[] = {0, 1, 2, 3, 4}; +static u32 mmc_mux_table[] = {0}; +static u32 mmc2_mux_table[] = {0, 2}; +static u32 pwm_mux_table[] = {0, 1, 2, 3}; + +static const struct hisi_mux_clock hi3516cv300_mux_clks[] = { + { HI3516CV300_UART_MUX, "uart_mux", uart_mux_p, ARRAY_SIZE(uart_mux_p), + CLK_SET_RATE_PARENT, 0xe4, 19, 1, 0, uart_mux_table, }, + { HI3516CV300_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), + CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, + { HI3516CV300_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 4, 2, 0, mmc_mux_table, }, + { HI3516CV300_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 12, 2, 0, mmc_mux_table, }, + { HI3516CV300_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 20, 2, 0, mmc2_mux_table, }, + { HI3516CV300_MMC3_MUX, "mmc3_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc8, 4, 2, 0, mmc_mux_table, }, + { HI3516CV300_PWM_MUX, "pwm_mux", pwm_mux_p, ARRAY_SIZE(pwm_mux_p), + CLK_SET_RATE_PARENT, 0x38, 2, 2, 0, pwm_mux_table, }, +}; + +static const struct hisi_gate_clock hi3516cv300_gate_clks[] = { + + { HI3516CV300_UART0_CLK, "clk_uart0", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 15, 0, }, + { HI3516CV300_UART1_CLK, "clk_uart1", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 16, 0, }, + { HI3516CV300_UART2_CLK, "clk_uart2", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 17, 0, }, + + { HI3516CV300_SPI0_CLK, "clk_spi0", "100m", CLK_SET_RATE_PARENT, + 0xe4, 13, 0, }, + { HI3516CV300_SPI1_CLK, "clk_spi1", "100m", CLK_SET_RATE_PARENT, + 0xe4, 14, 0, }, + + { HI3516CV300_FMC_CLK, "clk_fmc", "fmc_mux", CLK_SET_RATE_PARENT, + 0xc0, 1, 0, }, + { HI3516CV300_MMC0_CLK, "clk_mmc0", "mmc0_mux", CLK_SET_RATE_PARENT, + 0xc4, 1, 0, }, + { HI3516CV300_MMC1_CLK, "clk_mmc1", "mmc1_mux", CLK_SET_RATE_PARENT, + 0xc4, 9, 0, }, + { HI3516CV300_MMC2_CLK, "clk_mmc2", "mmc2_mux", CLK_SET_RATE_PARENT, + 0xc4, 17, 0, }, + { HI3516CV300_MMC3_CLK, "clk_mmc3", "mmc3_mux", CLK_SET_RATE_PARENT, + 0xc8, 1, 0, }, + + { HI3516CV300_ETH_CLK, "clk_eth", NULL, 0, 0xec, 1, 0, }, + + { HI3516CV300_DMAC_CLK, "clk_dmac", NULL, 0, 0xd8, 5, 0, }, + { HI3516CV300_PWM_CLK, "clk_pwm", "pwm_mux", CLK_SET_RATE_PARENT, + 0x38, 1, 0, }, + + { HI3516CV300_USB2_BUS_CLK, "clk_usb2_bus", NULL, 0, 0xb8, 0, 0, }, + { HI3516CV300_USB2_OHCI48M_CLK, "clk_usb2_ohci48m", NULL, 0, + 0xb8, 1, 0, }, + { HI3516CV300_USB2_OHCI12M_CLK, "clk_usb2_ohci12m", NULL, 0, + 0xb8, 2, 0, }, + { HI3516CV300_USB2_OTG_UTMI_CLK, "clk_usb2_otg_utmi", NULL, 0, + 0xb8, 3, 0, }, + { HI3516CV300_USB2_HST_PHY_CLK, "clk_usb2_hst_phy", NULL, 0, + 0xb8, 4, 0, }, + { HI3516CV300_USB2_UTMI0_CLK, "clk_usb2_utmi0", NULL, 0, 0xb8, 5, 0, }, + { HI3516CV300_USB2_PHY_CLK, "clk_usb2_phy", NULL, 0, 0xb8, 7, 0, }, +}; + +static struct hisi_clock_data *hi3516cv300_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3516CV300_CRG_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), clk_data); +unregister_mux: + hisi_clk_unregister_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), clk_data); +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data); + return ERR_PTR(ret); +} + +static void hi3516cv300_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), crg->clk_data); + hisi_clk_unregister_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), crg->clk_data); +} + +static const struct hisi_crg_funcs hi3516cv300_crg_funcs = { + .register_clks = hi3516cv300_clk_register, + .unregister_clks = hi3516cv300_clk_unregister, +}; + +/* hi3516CV300 sysctrl CRG */ +#define HI3516CV300_SYSCTRL_NR_CLKS 16 + +static const char *const wdt_mux_p[] __initconst = { "3m", "apb" }; +static u32 wdt_mux_table[] = {0, 1}; + +static const struct hisi_mux_clock hi3516cv300_sysctrl_mux_clks[] = { + { HI3516CV300_WDT_CLK, "wdt", wdt_mux_p, ARRAY_SIZE(wdt_mux_p), + CLK_SET_RATE_PARENT, 0x0, 23, 1, 0, wdt_mux_table, }, +}; + +static struct hisi_clock_data *hi3516cv300_sysctrl_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3516CV300_SYSCTRL_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data); + if (ret) + return ERR_PTR(ret); + + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_mux; + + return clk_data; + +unregister_mux: + hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data); + return ERR_PTR(ret); +} + +static void hi3516cv300_sysctrl_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3516cv300_sysctrl_funcs = { + .register_clks = hi3516cv300_sysctrl_clk_register, + .unregister_clks = hi3516cv300_sysctrl_clk_unregister, +}; + +static const struct of_device_id hi3516cv300_crg_match_table[] = { + { + .compatible = "hisilicon,hi3516cv300-crg", + .data = &hi3516cv300_crg_funcs + }, + { + .compatible = "hisilicon,hi3516cv300-sysctrl", + .data = &hi3516cv300_sysctrl_funcs + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3516cv300_crg_match_table); + +static int hi3516cv300_crg_probe(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->funcs = of_device_get_match_data(&pdev->dev); + if (!crg->funcs) + return -ENOENT; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = crg->funcs->register_clks(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3516cv300_crg_remove(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + return 0; +} + +static struct platform_driver hi3516cv300_crg_driver = { + .probe = hi3516cv300_crg_probe, + .remove = hi3516cv300_crg_remove, + .driver = { + .name = "hi3516cv300-crg", + .of_match_table = hi3516cv300_crg_match_table, + }, +}; + +static int __init hi3516cv300_crg_init(void) +{ + return platform_driver_register(&hi3516cv300_crg_driver); +} +core_initcall(hi3516cv300_crg_init); + +static void __exit hi3516cv300_crg_exit(void) +{ + platform_driver_unregister(&hi3516cv300_crg_driver); +} +module_exit(hi3516cv300_crg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3516CV300 CRG Driver"); diff --git a/drivers/clk/hisilicon/crg-hi3798cv200.c b/drivers/clk/hisilicon/crg-hi3798cv200.c new file mode 100644 index 000000000..4fe0b2a9b --- /dev/null +++ b/drivers/clk/hisilicon/crg-hi3798cv200.c @@ -0,0 +1,413 @@ +/* + * Hi3798CV200 Clock and Reset Generator Driver + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/histb-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "crg.h" +#include "reset.h" + +/* hi3798CV200 core CRG */ +#define HI3798CV200_INNER_CLK_OFFSET 64 +#define HI3798CV200_FIXED_24M 65 +#define HI3798CV200_FIXED_25M 66 +#define HI3798CV200_FIXED_50M 67 +#define HI3798CV200_FIXED_75M 68 +#define HI3798CV200_FIXED_100M 69 +#define HI3798CV200_FIXED_150M 70 +#define HI3798CV200_FIXED_200M 71 +#define HI3798CV200_FIXED_250M 72 +#define HI3798CV200_FIXED_300M 73 +#define HI3798CV200_FIXED_400M 74 +#define HI3798CV200_MMC_MUX 75 +#define HI3798CV200_ETH_PUB_CLK 76 +#define HI3798CV200_ETH_BUS_CLK 77 +#define HI3798CV200_ETH_BUS0_CLK 78 +#define HI3798CV200_ETH_BUS1_CLK 79 +#define HI3798CV200_COMBPHY1_MUX 80 +#define HI3798CV200_FIXED_12M 81 +#define HI3798CV200_FIXED_48M 82 +#define HI3798CV200_FIXED_60M 83 +#define HI3798CV200_FIXED_166P5M 84 +#define HI3798CV200_SDIO0_MUX 85 +#define HI3798CV200_COMBPHY0_MUX 86 + +#define HI3798CV200_CRG_NR_CLKS 128 + +static const struct hisi_fixed_rate_clock hi3798cv200_fixed_rate_clks[] = { + { HISTB_OSC_CLK, "clk_osc", NULL, 0, 24000000, }, + { HISTB_APB_CLK, "clk_apb", NULL, 0, 100000000, }, + { HISTB_AHB_CLK, "clk_ahb", NULL, 0, 200000000, }, + { HI3798CV200_FIXED_12M, "12m", NULL, 0, 12000000, }, + { HI3798CV200_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3798CV200_FIXED_25M, "25m", NULL, 0, 25000000, }, + { HI3798CV200_FIXED_48M, "48m", NULL, 0, 48000000, }, + { HI3798CV200_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3798CV200_FIXED_60M, "60m", NULL, 0, 60000000, }, + { HI3798CV200_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HI3798CV200_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HI3798CV200_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3798CV200_FIXED_166P5M, "166p5m", NULL, 0, 165000000, }, + { HI3798CV200_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HI3798CV200_FIXED_250M, "250m", NULL, 0, 250000000, }, +}; + +static const char *const mmc_mux_p[] = { + "100m", "50m", "25m", "200m", "150m" }; +static u32 mmc_mux_table[] = {0, 1, 2, 3, 6}; + +static const char *const comphy_mux_p[] = { + "100m", "25m"}; +static u32 comphy_mux_table[] = {2, 3}; + +static const char *const sdio_mux_p[] = { + "100m", "50m", "150m", "166p5m" }; +static u32 sdio_mux_table[] = {0, 1, 2, 3}; + +static struct hisi_mux_clock hi3798cv200_mux_clks[] = { + { HI3798CV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, }, + { HI3798CV200_COMBPHY0_MUX, "combphy0_mux", + comphy_mux_p, ARRAY_SIZE(comphy_mux_p), + CLK_SET_RATE_PARENT, 0x188, 2, 2, 0, comphy_mux_table, }, + { HI3798CV200_COMBPHY1_MUX, "combphy1_mux", + comphy_mux_p, ARRAY_SIZE(comphy_mux_p), + CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy_mux_table, }, + { HI3798CV200_SDIO0_MUX, "sdio0_mux", sdio_mux_p, + ARRAY_SIZE(sdio_mux_p), CLK_SET_RATE_PARENT, + 0x9c, 8, 2, 0, sdio_mux_table, }, +}; + +static u32 mmc_phase_regvals[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static u32 mmc_phase_degrees[] = {0, 45, 90, 135, 180, 225, 270, 315}; + +static struct hisi_phase_clock hi3798cv200_phase_clks[] = { + { HISTB_MMC_SAMPLE_CLK, "mmc_sample", "clk_mmc_ciu", + CLK_SET_RATE_PARENT, 0xa0, 12, 3, mmc_phase_degrees, + mmc_phase_regvals, ARRAY_SIZE(mmc_phase_regvals) }, + { HISTB_MMC_DRV_CLK, "mmc_drive", "clk_mmc_ciu", + CLK_SET_RATE_PARENT, 0xa0, 16, 3, mmc_phase_degrees, + mmc_phase_regvals, ARRAY_SIZE(mmc_phase_regvals) }, +}; + +static const struct hisi_gate_clock hi3798cv200_gate_clks[] = { + /* UART */ + { HISTB_UART2_CLK, "clk_uart2", "75m", + CLK_SET_RATE_PARENT, 0x68, 4, 0, }, + /* I2C */ + { HISTB_I2C0_CLK, "clk_i2c0", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 4, 0, }, + { HISTB_I2C1_CLK, "clk_i2c1", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 8, 0, }, + { HISTB_I2C2_CLK, "clk_i2c2", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 12, 0, }, + { HISTB_I2C3_CLK, "clk_i2c3", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 16, 0, }, + { HISTB_I2C4_CLK, "clk_i2c4", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 20, 0, }, + /* SPI */ + { HISTB_SPI0_CLK, "clk_spi0", "clk_apb", + CLK_SET_RATE_PARENT, 0x70, 0, 0, }, + /* SDIO */ + { HISTB_SDIO0_BIU_CLK, "clk_sdio0_biu", "200m", + CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, + { HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "sdio0_mux", + CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, + /* EMMC */ + { HISTB_MMC_BIU_CLK, "clk_mmc_biu", "200m", + CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, + { HISTB_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, + /* PCIE*/ + { HISTB_PCIE_BUS_CLK, "clk_pcie_bus", "200m", + CLK_SET_RATE_PARENT, 0x18c, 0, 0, }, + { HISTB_PCIE_SYS_CLK, "clk_pcie_sys", "100m", + CLK_SET_RATE_PARENT, 0x18c, 1, 0, }, + { HISTB_PCIE_PIPE_CLK, "clk_pcie_pipe", "250m", + CLK_SET_RATE_PARENT, 0x18c, 2, 0, }, + { HISTB_PCIE_AUX_CLK, "clk_pcie_aux", "24m", + CLK_SET_RATE_PARENT, 0x18c, 3, 0, }, + /* Ethernet */ + { HI3798CV200_ETH_PUB_CLK, "clk_pub", NULL, + CLK_SET_RATE_PARENT, 0xcc, 5, 0, }, + { HI3798CV200_ETH_BUS_CLK, "clk_bus", "clk_pub", + CLK_SET_RATE_PARENT, 0xcc, 0, 0, }, + { HI3798CV200_ETH_BUS0_CLK, "clk_bus_m0", "clk_bus", + CLK_SET_RATE_PARENT, 0xcc, 1, 0, }, + { HI3798CV200_ETH_BUS1_CLK, "clk_bus_m1", "clk_bus", + CLK_SET_RATE_PARENT, 0xcc, 2, 0, }, + { HISTB_ETH0_MAC_CLK, "clk_mac0", "clk_bus_m0", + CLK_SET_RATE_PARENT, 0xcc, 3, 0, }, + { HISTB_ETH0_MACIF_CLK, "clk_macif0", "clk_bus_m0", + CLK_SET_RATE_PARENT, 0xcc, 24, 0, }, + { HISTB_ETH1_MAC_CLK, "clk_mac1", "clk_bus_m1", + CLK_SET_RATE_PARENT, 0xcc, 4, 0, }, + { HISTB_ETH1_MACIF_CLK, "clk_macif1", "clk_bus_m1", + CLK_SET_RATE_PARENT, 0xcc, 25, 0, }, + /* COMBPHY0 */ + { HISTB_COMBPHY0_CLK, "clk_combphy0", "combphy0_mux", + CLK_SET_RATE_PARENT, 0x188, 0, 0, }, + /* COMBPHY1 */ + { HISTB_COMBPHY1_CLK, "clk_combphy1", "combphy1_mux", + CLK_SET_RATE_PARENT, 0x188, 8, 0, }, + /* USB2 */ + { HISTB_USB2_BUS_CLK, "clk_u2_bus", "clk_ahb", + CLK_SET_RATE_PARENT, 0xb8, 0, 0, }, + { HISTB_USB2_PHY_CLK, "clk_u2_phy", "60m", + CLK_SET_RATE_PARENT, 0xb8, 4, 0, }, + { HISTB_USB2_12M_CLK, "clk_u2_12m", "12m", + CLK_SET_RATE_PARENT, 0xb8, 2, 0 }, + { HISTB_USB2_48M_CLK, "clk_u2_48m", "48m", + CLK_SET_RATE_PARENT, 0xb8, 1, 0 }, + { HISTB_USB2_UTMI_CLK, "clk_u2_utmi", "60m", + CLK_SET_RATE_PARENT, 0xb8, 5, 0 }, + { HISTB_USB2_OTG_UTMI_CLK, "clk_u2_otg_utmi", "60m", + CLK_SET_RATE_PARENT, 0xb8, 3, 0 }, + { HISTB_USB2_PHY1_REF_CLK, "clk_u2_phy1_ref", "24m", + CLK_SET_RATE_PARENT, 0xbc, 0, 0 }, + { HISTB_USB2_PHY2_REF_CLK, "clk_u2_phy2_ref", "24m", + CLK_SET_RATE_PARENT, 0xbc, 2, 0 }, + /* USB3 */ + { HISTB_USB3_BUS_CLK, "clk_u3_bus", NULL, + CLK_SET_RATE_PARENT, 0xb0, 0, 0 }, + { HISTB_USB3_UTMI_CLK, "clk_u3_utmi", NULL, + CLK_SET_RATE_PARENT, 0xb0, 4, 0 }, + { HISTB_USB3_PIPE_CLK, "clk_u3_pipe", NULL, + CLK_SET_RATE_PARENT, 0xb0, 3, 0 }, + { HISTB_USB3_SUSPEND_CLK, "clk_u3_suspend", NULL, + CLK_SET_RATE_PARENT, 0xb0, 2, 0 }, + { HISTB_USB3_BUS_CLK1, "clk_u3_bus1", NULL, + CLK_SET_RATE_PARENT, 0xb0, 16, 0 }, + { HISTB_USB3_UTMI_CLK1, "clk_u3_utmi1", NULL, + CLK_SET_RATE_PARENT, 0xb0, 20, 0 }, + { HISTB_USB3_PIPE_CLK1, "clk_u3_pipe1", NULL, + CLK_SET_RATE_PARENT, 0xb0, 19, 0 }, + { HISTB_USB3_SUSPEND_CLK1, "clk_u3_suspend1", NULL, + CLK_SET_RATE_PARENT, 0xb0, 18, 0 }, +}; + +static struct hisi_clock_data *hi3798cv200_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3798CV200_CRG_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + /* hisi_phase_clock is resource managed */ + ret = hisi_clk_register_phase(&pdev->dev, + hi3798cv200_phase_clks, + ARRAY_SIZE(hi3798cv200_phase_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + clk_data); +unregister_mux: + hisi_clk_unregister_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + clk_data); +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + clk_data); + return ERR_PTR(ret); +} + +static void hi3798cv200_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + crg->clk_data); + hisi_clk_unregister_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3798cv200_crg_funcs = { + .register_clks = hi3798cv200_clk_register, + .unregister_clks = hi3798cv200_clk_unregister, +}; + +/* hi3798CV200 sysctrl CRG */ + +#define HI3798CV200_SYSCTRL_NR_CLKS 16 + +static const struct hisi_gate_clock hi3798cv200_sysctrl_gate_clks[] = { + { HISTB_IR_CLK, "clk_ir", "24m", + CLK_SET_RATE_PARENT, 0x48, 4, 0, }, + { HISTB_TIMER01_CLK, "clk_timer01", "24m", + CLK_SET_RATE_PARENT, 0x48, 6, 0, }, + { HISTB_UART0_CLK, "clk_uart0", "75m", + CLK_SET_RATE_PARENT, 0x48, 10, 0, }, +}; + +static struct hisi_clock_data *hi3798cv200_sysctrl_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3798CV200_SYSCTRL_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + clk_data); + return ERR_PTR(ret); +} + +static void hi3798cv200_sysctrl_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3798cv200_sysctrl_funcs = { + .register_clks = hi3798cv200_sysctrl_clk_register, + .unregister_clks = hi3798cv200_sysctrl_clk_unregister, +}; + +static const struct of_device_id hi3798cv200_crg_match_table[] = { + { .compatible = "hisilicon,hi3798cv200-crg", + .data = &hi3798cv200_crg_funcs }, + { .compatible = "hisilicon,hi3798cv200-sysctrl", + .data = &hi3798cv200_sysctrl_funcs }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3798cv200_crg_match_table); + +static int hi3798cv200_crg_probe(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->funcs = of_device_get_match_data(&pdev->dev); + if (!crg->funcs) + return -ENOENT; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = crg->funcs->register_clks(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3798cv200_crg_remove(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + return 0; +} + +static struct platform_driver hi3798cv200_crg_driver = { + .probe = hi3798cv200_crg_probe, + .remove = hi3798cv200_crg_remove, + .driver = { + .name = "hi3798cv200-crg", + .of_match_table = hi3798cv200_crg_match_table, + }, +}; + +static int __init hi3798cv200_crg_init(void) +{ + return platform_driver_register(&hi3798cv200_crg_driver); +} +core_initcall(hi3798cv200_crg_init); + +static void __exit hi3798cv200_crg_exit(void) +{ + platform_driver_unregister(&hi3798cv200_crg_driver); +} +module_exit(hi3798cv200_crg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3798CV200 CRG Driver"); diff --git a/drivers/clk/hisilicon/crg.h b/drivers/clk/hisilicon/crg.h new file mode 100644 index 000000000..e0739717d --- /dev/null +++ b/drivers/clk/hisilicon/crg.h @@ -0,0 +1,34 @@ +/* + * HiSilicon Clock and Reset Driver Header + * + * Copyright (c) 2016 HiSilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __HISI_CRG_H +#define __HISI_CRG_H + +struct hisi_clock_data; +struct hisi_reset_controller; + +struct hisi_crg_funcs { + struct hisi_clock_data* (*register_clks)(struct platform_device *pdev); + void (*unregister_clks)(struct platform_device *pdev); +}; + +struct hisi_crg_dev { + struct hisi_clock_data *clk_data; + struct hisi_reset_controller *rstc; + const struct hisi_crg_funcs *funcs; +}; + +#endif /* __HISI_CRG_H */ diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c new file mode 100644 index 000000000..43e82fa64 --- /dev/null +++ b/drivers/clk/hisilicon/reset.c @@ -0,0 +1,132 @@ +/* + * Hisilicon Reset Controller Driver + * + * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "reset.h" + +#define HISI_RESET_BIT_MASK 0x1f +#define HISI_RESET_OFFSET_SHIFT 8 +#define HISI_RESET_OFFSET_MASK 0xffff00 + +struct hisi_reset_controller { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + + +#define to_hisi_reset_controller(rcdev) \ + container_of(rcdev, struct hisi_reset_controller, rcdev) + +static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + u32 offset; + u8 bit; + + offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT) + & HISI_RESET_OFFSET_MASK; + bit = reset_spec->args[1] & HISI_RESET_BIT_MASK; + + return (offset | bit); +} + +static int hisi_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); + unsigned long flags; + u32 offset, reg; + u8 bit; + + offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; + bit = id & HISI_RESET_BIT_MASK; + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc->membase + offset); + writel(reg | BIT(bit), rstc->membase + offset); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; +} + +static int hisi_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); + unsigned long flags; + u32 offset, reg; + u8 bit; + + offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; + bit = id & HISI_RESET_BIT_MASK; + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc->membase + offset); + writel(reg & ~BIT(bit), rstc->membase + offset); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; +} + +static const struct reset_control_ops hisi_reset_ops = { + .assert = hisi_reset_assert, + .deassert = hisi_reset_deassert, +}; + +struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) +{ + struct hisi_reset_controller *rstc; + struct resource *res; + + rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rstc->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rstc->membase)) + return NULL; + + spin_lock_init(&rstc->lock); + rstc->rcdev.owner = THIS_MODULE; + rstc->rcdev.ops = &hisi_reset_ops; + rstc->rcdev.of_node = pdev->dev.of_node; + rstc->rcdev.of_reset_n_cells = 2; + rstc->rcdev.of_xlate = hisi_reset_of_xlate; + reset_controller_register(&rstc->rcdev); + + return rstc; +} +EXPORT_SYMBOL_GPL(hisi_reset_init); + +void hisi_reset_exit(struct hisi_reset_controller *rstc) +{ + reset_controller_unregister(&rstc->rcdev); +} +EXPORT_SYMBOL_GPL(hisi_reset_exit); diff --git a/drivers/clk/hisilicon/reset.h b/drivers/clk/hisilicon/reset.h new file mode 100644 index 000000000..9a69374a0 --- /dev/null +++ b/drivers/clk/hisilicon/reset.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __HISI_RESET_H +#define __HISI_RESET_H + +struct device_node; +struct hisi_reset_controller; + +#ifdef CONFIG_RESET_CONTROLLER +struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev); +void hisi_reset_exit(struct hisi_reset_controller *rstc); +#else +static inline +struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) +{ + return 0; +} +static inline void hisi_reset_exit(struct hisi_reset_controller *rstc) +{} +#endif + +#endif /* __HISI_RESET_H */ |