Message ID | 1428916660-25910-5-git-send-email-bintian.wang@huawei.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, 2015-04-13 at 17:17 +0800, Bintian Wang wrote: > --- /dev/null > +++ b/drivers/clk/hisilicon/Kconfig > @@ -0,0 +1,5 @@ > +config COMMON_CLK_HI6220 > + tristate "Hi6220 Clock Driver" > + depends on OF && ARCH_HISI > + help > + Build the Hisilicon Hi6220 clock driver based on the common clock framework. In 5/6 you make arm64's ARCH_HISI (a bool) select COMMON_CLK_HI6220. So for arm64 this driver will always be built-in. For arm's ARCH_HISI it's possible to set COMMON_CLK_HI6220 to 'm'. > --- a/drivers/clk/hisilicon/Makefile > +++ b/drivers/clk/hisilicon/Makefile > @@ -2,8 +2,9 @@ > # Hisilicon Clock specific Makefile > # > > -obj-y += clk.o clkgate-separated.o > +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o These objects will always be built-in, right? > 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_HI6220) += clk-hi6220.o If CONFIG_COMMON_CLK_HI6220 is 'm' this will build a module named clk-hi6220.ko. If I try to do that I get: $ make -C ../../.. ARCH=arm CROSS_COMPILE=arm-linux-gnu- M=$PWD clk-hi6220.ko make: Entering directory `[...]' CC [M] [...]/drivers/clk/hisilicon/clk-hi6220.o MODPOST 1 modules WARNING: "hisi_clk_register_gate" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hi6220_clk_register_divider" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hisi_clk_register_mux" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hisi_clk_register_gate_sep" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hisi_clk_register_fixed_factor" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hisi_clk_register_fixed_rate" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! WARNING: "hisi_clk_init" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! CC [...]/drivers/clk/hisilicon/clk-hi6220.mod.o LDFINAL [M] [...]/drivers/clk/hisilicon/clk-hi6220.ko make: Leaving directory `[...]' That is, I think, because nothing exports these symbols. > --- /dev/null > +++ b/drivers/clk/hisilicon/clk-hi6220.c > @@ -0,0 +1,292 @@ > +/* > + * 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 <linux/clk.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, CLK_IS_ROOT, 32764, }, > + { HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, }, > + { HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, }, > + { HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, }, > + { HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, }, > + { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, > + { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, > + { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, > + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, > + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, > + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, > + { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, > + { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 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", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, > + { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, > + { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", 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, }, > +}; > + > +static struct hisi_clock_data *clk_data_ao; > + > +static void __init hi6220_clk_ao_init(struct device_node *np) > +{ > + 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,aoctrl", hi6220_clk_ao_init); > + > + > +/* clocks in sysctrl */ > +static const char *mmc0_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; > +static const char *mmc0_mux1_p[] __initconst = { "mmc0_mux0", "pll_media_gate", }; > +static const char *mmc0_src_p[] __initconst = { "mmc0srcsel", "mmc0_div", }; > +static const char *mmc1_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; > +static const char *mmc1_mux1_p[] __initconst = { "mmc1_mux0", "pll_media_gate", }; > +static const char *mmc1_src_p[] __initconst = { "mmc1srcsel", "mmc1_div", }; > +static const char *mmc2_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; > +static const char *mmc2_mux1_p[] __initconst = { "mmc2_mux0", "pll_media_gate", }; > +static const char *mmc2_src_p[] __initconst = { "mmc2srcsel", "mmc2_div", }; > +static const char *mmc0_sample_in[] __initconst = { "mmc0_sample", "mmc0_pad", }; > +static const char *mmc1_sample_in[] __initconst = { "mmc1_sample", "mmc1_pad", }; > +static const char *mmc2_sample_in[] __initconst = { "mmc2_sample", "mmc2_pad", }; > +static const char *uart1_src[] __initconst = { "clk_tcxo", "clk_150m", }; > +static const char *uart2_src[] __initconst = { "clk_tcxo", "clk_150m", }; > +static const char *uart3_src[] __initconst = { "clk_tcxo", "clk_150m", }; > +static const char *uart4_src[] __initconst = { "clk_tcxo", "clk_150m", }; > +static const char *hifi_src[] __initconst = { "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_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_IGNORE_UNUSED, 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); > + > + if (!clk_data_ao) > + return; > + > + /* enable high speed clock on UART1 mux */ > + clk_set_parent(clk_data->clk_data.clks[HI6220_UART1_SRC], > + clk_data_ao->clk_data.clks[HI6220_150M]); > +} > +CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,sysctrl", hi6220_clk_sys_init); > + > + > +/* clocks in media controller */ > +static const char *clk_1000_1200_src[] __initconst = { "pll_gpu_gate", "media_syspll_src", }; > +static const char *clk_1440_1200_src[] __initconst = { "media_syspll_src", "media_pll_src", }; > +static const char *clk_1000_1440_src[] __initconst = { "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(hi6220_clk_media, "hisilicon,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,pmctrl", hi6220_clk_power_init); There's nothing module specific in this file. And the lack of a MODULE_LICENSE() macro is also telling. If this was built as a module loading that module - ignoring the undefined symbols - would taint the kernel. It seems to me that COMMON_CLK_HI6220 is meant to be a bool symbol. Paul Bolle I wonder what checkpatch had to say about the length of the lines seen in this patch.
Hello Paul, Thank you very much for code review and testing on arm! On 2015/4/13 19:56, Paul Bolle wrote: > On Mon, 2015-04-13 at 17:17 +0800, Bintian Wang wrote: >> --- /dev/null >> +++ b/drivers/clk/hisilicon/Kconfig >> @@ -0,0 +1,5 @@ >> +config COMMON_CLK_HI6220 >> + tristate "Hi6220 Clock Driver" >> + depends on OF && ARCH_HISI >> + help >> + Build the Hisilicon Hi6220 clock driver based on the common clock framework. > > In 5/6 you make arm64's ARCH_HISI (a bool) select COMMON_CLK_HI6220. So > for arm64 this driver will always be built-in. > > For arm's ARCH_HISI it's possible to set COMMON_CLK_HI6220 to 'm'. Setting COMMON_CLK_HI6220 to a bool symbol is a good solution based on current code base, I will fix it in next version. > >> --- a/drivers/clk/hisilicon/Makefile >> +++ b/drivers/clk/hisilicon/Makefile >> @@ -2,8 +2,9 @@ >> # Hisilicon Clock specific Makefile >> # >> >> -obj-y += clk.o clkgate-separated.o >> +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o > > These objects will always be built-in, right? > >> 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_HI6220) += clk-hi6220.o > > If CONFIG_COMMON_CLK_HI6220 is 'm' this will build a module named > clk-hi6220.ko. If I try to do that I get: > $ make -C ../../.. ARCH=arm CROSS_COMPILE=arm-linux-gnu- M=$PWD clk-hi6220.ko > make: Entering directory `[...]' > CC [M] [...]/drivers/clk/hisilicon/clk-hi6220.o > MODPOST 1 modules > WARNING: "hisi_clk_register_gate" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hi6220_clk_register_divider" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hisi_clk_register_mux" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hisi_clk_register_gate_sep" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hisi_clk_register_fixed_factor" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hisi_clk_register_fixed_rate" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > WARNING: "hisi_clk_init" [[...]/drivers/clk/hisilicon/clk-hi6220.ko] undefined! > CC [...]/drivers/clk/hisilicon/clk-hi6220.mod.o > LDFINAL [M] [...]/drivers/clk/hisilicon/clk-hi6220.ko > make: Leaving directory `[...]' > > That is, I think, because nothing exports these symbols. [...] > There's nothing module specific in this file. And the lack of a > MODULE_LICENSE() macro is also telling. If this was built as a module > loading that module - ignoring the undefined symbols - would taint the > kernel. > > It seems to me that COMMON_CLK_HI6220 is meant to be a bool symbol. You are right. > > > Paul Bolle > > I wonder what checkpatch had to say about the length of the lines seen > in this patch. Yes, I ran this script before sending out this patch set, it reports warnings about "line over 80 characters ", but I find it's easier to read than shrinking one line to several lines, so just keep it, do I need to fix it? Thanks, Bintian > > . >
On Monday 13 April 2015 17:17:38 Bintian Wang wrote: > +#define HI6220_CFG_CSI2PHY 8 > +#define HI6220_ISP_SCLK_GATE 9 > +#define HI6220_ISP_SCLK_GATE1 10 > +#define HI6220_ADE_CORE_GATE 11 > +#define HI6220_CODEC_VPU_GATE 12 > +#define HI6220_MED_SYSPLL 13 > + > +/* mux clocks */ > +#define HI6220_1440_1200 20 > +#define HI6220_1000_1200 21 > +#define HI6220_1000_1440 22 > + > +/* divider clocks */ > +#define HI6220_CODEC_JPEG 30 > +#define HI6220_ISP_SCLK_SRC 31 > +#define HI6220_ISP_SCLK1 32 > The numbers seem rather arbitrary, and you have both holes as well as duplicate numbers here. I would suggest you do one of two things instead: a) have a separate header file per clock driver and make all the numbers unique and consecutive within that header b) use the same numbers as the hardware registers so you can put the numbers directly into the dts and don't need a header to create an artificial ABI between the clock driver and the boot loader. Arnd
Hello Arnd, Thanks for your code review. On 2015/4/13 21:30, Arnd Bergmann wrote: > On Monday 13 April 2015 17:17:38 Bintian Wang wrote: >> +#define HI6220_CFG_CSI2PHY 8 >> +#define HI6220_ISP_SCLK_GATE 9 >> +#define HI6220_ISP_SCLK_GATE1 10 >> +#define HI6220_ADE_CORE_GATE 11 >> +#define HI6220_CODEC_VPU_GATE 12 >> +#define HI6220_MED_SYSPLL 13 >> + >> +/* mux clocks */ >> +#define HI6220_1440_1200 20 >> +#define HI6220_1000_1200 21 >> +#define HI6220_1000_1440 22 >> + >> +/* divider clocks */ >> +#define HI6220_CODEC_JPEG 30 >> +#define HI6220_ISP_SCLK_SRC 31 >> +#define HI6220_ISP_SCLK1 32 >> > > The numbers seem rather arbitrary, and you have both holes as well as duplicate > numbers here. I would suggest you do one of two things instead: I just worry about some special clocks may be added later so keep some holes for them; The duplicate numbers means clocks belong to different system control domains. > > a) have a separate header file per clock driver and make all the > numbers unique and consecutive within that header > > b) use the same numbers as the hardware registers so you can put the > numbers directly into the dts and don't need a header to create > an artificial ABI between the clock driver and the boot loader. This header file will be used by device tree (I like using the clock name instead "magic number" in dts :) ), so how about keep them in one header file and let dts just include one header file (not four files), but remove the holes? Thank you Arnd. BR, Bintian > > Arnd > > . >
On Mon, 2015-04-13 at 21:17 +0800, Bintian wrote: > Thank you very much for code review and testing on arm! s/testing/crosscompiling for/ > On 2015/4/13 19:56, Paul Bolle wrote: > > I wonder what checkpatch had to say about the length of the lines seen > > in this patch. > > Yes, I ran this script before sending out this patch set, it reports > warnings about "line over 80 characters ", but I find it's easier to > read than shrinking one line to several lines, so just keep it, do I > need to fix it? I'll leave that to the maintainers of drivers/clk/. (I don't care deeply. So on second thought I should not have made that remark.) Thanks, Paul Bolle
On Monday 13 April 2015 21:57:46 Bintian wrote: > Hello Arnd, > > Thanks for your code review. > > On 2015/4/13 21:30, Arnd Bergmann wrote: > > On Monday 13 April 2015 17:17:38 Bintian Wang wrote: > >> +#define HI6220_CFG_CSI2PHY 8 > >> +#define HI6220_ISP_SCLK_GATE 9 > >> +#define HI6220_ISP_SCLK_GATE1 10 > >> +#define HI6220_ADE_CORE_GATE 11 > >> +#define HI6220_CODEC_VPU_GATE 12 > >> +#define HI6220_MED_SYSPLL 13 > >> + > >> +/* mux clocks */ > >> +#define HI6220_1440_1200 20 > >> +#define HI6220_1000_1200 21 > >> +#define HI6220_1000_1440 22 > >> + > >> +/* divider clocks */ > >> +#define HI6220_CODEC_JPEG 30 > >> +#define HI6220_ISP_SCLK_SRC 31 > >> +#define HI6220_ISP_SCLK1 32 > >> > > > > The numbers seem rather arbitrary, and you have both holes as well as duplicate > > numbers here. I would suggest you do one of two things instead: > I just worry about some special clocks may be added later so keep some > holes for them; > > The duplicate numbers means clocks belong to different system control > domains. I don't understand. How would there be additional clocks added later? Wouldn't that be a new chip? If you have separate system control domains, doesn't that mean that you also have separate DT bindings? > > a) have a separate header file per clock driver and make all the > > numbers unique and consecutive within that header > > > > b) use the same numbers as the hardware registers so you can put the > > numbers directly into the dts and don't need a header to create > > an artificial ABI between the clock driver and the boot loader. > This header file will be used by device tree (I like using the clock > name instead "magic number" in dts :) ) That's not how it works though: The dts file is the place to define the hardware numbers, we do that for all sorts of numbers: interrupts, gpios, register ranges etc are all defined in dts to avoid putting magic numbers in external header files. There are some cases where it gets too ugly for clock controllers that are highly irregular, but yours doesn't seem to be that kind. E.g. all the fixed rate clocks should just be separate device nodes, which lets you remove the binding for that node. > so how about keep them in one header file and let dts just include > one header file (not four files), but remove the holes? The header files constantly cause problems with merge dependencies, and we have some other companies that keep releasing new chips that each time require a new header file. If hisilicon plans to make more chips like this one, please consider coming up with more generic bindings to avoid this. Arnd
? 2015/4/13 23:34, Arnd Bergmann ??: > On Monday 13 April 2015 21:57:46 Bintian wrote: >> Hello Arnd, >> >> Thanks for your code review. >> >> On 2015/4/13 21:30, Arnd Bergmann wrote: >>> On Monday 13 April 2015 17:17:38 Bintian Wang wrote: >>>> +#define HI6220_CFG_CSI2PHY 8 >>>> +#define HI6220_ISP_SCLK_GATE 9 >>>> +#define HI6220_ISP_SCLK_GATE1 10 >>>> +#define HI6220_ADE_CORE_GATE 11 >>>> +#define HI6220_CODEC_VPU_GATE 12 >>>> +#define HI6220_MED_SYSPLL 13 >>>> + >>>> +/* mux clocks */ >>>> +#define HI6220_1440_1200 20 >>>> +#define HI6220_1000_1200 21 >>>> +#define HI6220_1000_1440 22 >>>> + >>>> +/* divider clocks */ >>>> +#define HI6220_CODEC_JPEG 30 >>>> +#define HI6220_ISP_SCLK_SRC 31 >>>> +#define HI6220_ISP_SCLK1 32 >>>> >>> >>> The numbers seem rather arbitrary, and you have both holes as well as duplicate >>> numbers here. I would suggest you do one of two things instead: > >> I just worry about some special clocks may be added later so keep some >> holes for them; >> >> The duplicate numbers means clocks belong to different system control >> domains. > > I don't understand. How would there be additional clocks added later? > Wouldn't that be a new chip? There are some clocks not used in linux system. e.g, some clocks are used in base band processor. So, some numbers are not defined here. > If you have separate system control domains, doesn't that mean that you > also have separate DT bindings? > >>> a) have a separate header file per clock driver and make all the >>> numbers unique and consecutive within that header >>> >>> b) use the same numbers as the hardware registers so you can put the >>> numbers directly into the dts and don't need a header to create >>> an artificial ABI between the clock driver and the boot loader. >> This header file will be used by device tree (I like using the clock >> name instead "magic number" in dts :) ) > > That's not how it works though: The dts file is the place to define > the hardware numbers, we do that for all sorts of numbers: interrupts, > gpios, register ranges etc are all defined in dts to avoid putting > magic numbers in external header files. > > There are some cases where it gets too ugly for clock controllers > that are highly irregular, but yours doesn't seem to be that kind. > > E.g. all the fixed rate clocks should just be separate device nodes, > which lets you remove the binding for that node. > >> so how about keep them in one header file and let dts just include >> one header file (not four files), but remove the holes? > > The header files constantly cause problems with merge dependencies, > and we have some other companies that keep releasing new chips > that each time require a new header file. If hisilicon plans to make > more chips like this one, please consider coming up with more > generic bindings to avoid this. > > Arnd > > . >
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 0b474a0..415fc31 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -145,6 +145,8 @@ config COMMON_CLK_CDCE706 source "drivers/clk/qcom/Kconfig" +source "drivers/clk/hisilicon/Kconfig" + endmenu source "drivers/clk/bcm/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..45fa028 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -45,9 +45,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ -obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ -obj-$(CONFIG_ARCH_HIP04) += hisilicon/ -obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ +obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 0000000..e3ead46 --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,5 @@ +config COMMON_CLK_HI6220 + tristate "Hi6220 Clock Driver" + depends on OF && ARCH_HISI + help + Build the Hisilicon Hi6220 clock driver based on the common clock framework. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 038c02f..48f0116 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -2,8 +2,9 @@ # Hisilicon Clock specific Makefile # -obj-y += clk.o clkgate-separated.o +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.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_HI6220) += clk-hi6220.o diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c new file mode 100644 index 0000000..ee85a3a --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -0,0 +1,292 @@ +/* + * 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 <linux/clk.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, CLK_IS_ROOT, 32764, }, + { HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, }, + { HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, }, + { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, + { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, + { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, + { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 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", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", 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, }, +}; + +static struct hisi_clock_data *clk_data_ao; + +static void __init hi6220_clk_ao_init(struct device_node *np) +{ + 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,aoctrl", hi6220_clk_ao_init); + + +/* clocks in sysctrl */ +static const char *mmc0_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; +static const char *mmc0_mux1_p[] __initconst = { "mmc0_mux0", "pll_media_gate", }; +static const char *mmc0_src_p[] __initconst = { "mmc0srcsel", "mmc0_div", }; +static const char *mmc1_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; +static const char *mmc1_mux1_p[] __initconst = { "mmc1_mux0", "pll_media_gate", }; +static const char *mmc1_src_p[] __initconst = { "mmc1srcsel", "mmc1_div", }; +static const char *mmc2_mux0_p[] __initconst = { "pll_ddr_gate", "syspll", }; +static const char *mmc2_mux1_p[] __initconst = { "mmc2_mux0", "pll_media_gate", }; +static const char *mmc2_src_p[] __initconst = { "mmc2srcsel", "mmc2_div", }; +static const char *mmc0_sample_in[] __initconst = { "mmc0_sample", "mmc0_pad", }; +static const char *mmc1_sample_in[] __initconst = { "mmc1_sample", "mmc1_pad", }; +static const char *mmc2_sample_in[] __initconst = { "mmc2_sample", "mmc2_pad", }; +static const char *uart1_src[] __initconst = { "clk_tcxo", "clk_150m", }; +static const char *uart2_src[] __initconst = { "clk_tcxo", "clk_150m", }; +static const char *uart3_src[] __initconst = { "clk_tcxo", "clk_150m", }; +static const char *uart4_src[] __initconst = { "clk_tcxo", "clk_150m", }; +static const char *hifi_src[] __initconst = { "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_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_IGNORE_UNUSED, 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); + + if (!clk_data_ao) + return; + + /* enable high speed clock on UART1 mux */ + clk_set_parent(clk_data->clk_data.clks[HI6220_UART1_SRC], + clk_data_ao->clk_data.clks[HI6220_150M]); +} +CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,sysctrl", hi6220_clk_sys_init); + + +/* clocks in media controller */ +static const char *clk_1000_1200_src[] __initconst = { "pll_gpu_gate", "media_syspll_src", }; +static const char *clk_1440_1200_src[] __initconst = { "media_syspll_src", "media_pll_src", }; +static const char *clk_1000_1440_src[] __initconst = { "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(hi6220_clk_media, "hisilicon,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,pmctrl", hi6220_clk_power_init); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a078e84..5d2305c 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -232,3 +232,32 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } + +void __init hi6220_clk_register_divider(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 index 31083ff..462a570 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -79,6 +79,18 @@ struct hisi_divider_clock { 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; @@ -94,6 +106,9 @@ 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 __init *hisi_clk_init(struct device_node *, int); void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, @@ -108,4 +123,6 @@ void __init hisi_clk_register_gate(struct hisi_gate_clock *, int, struct hisi_clock_data *); void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, int, struct hisi_clock_data *); +void __init hi6220_clk_register_divider(struct hi6220_divider_clock *, + int, struct hisi_clock_data *); #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 0000000..9e3825b --- /dev/null +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,273 @@ +/* + * 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> + +#define div_mask(width) ((1 << (width)) - 1) + +/* + * The reverse of DIV_ROUND_UP: The maximum number which + * divided by m is r + */ +#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 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 int hi6220_get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + +static unsigned int hi6220_get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + + return 0; +} + +static unsigned int hi6220_get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int div, val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + div = hi6220_get_table_div(dclk->table, val); + if (!div) { + pr_warn("%s: Invalid divisor for clock %s\n", __func__, + __clk_get_name(hw->clk)); + return parent_rate; + } + + return parent_rate / div; +} + +static bool hi6220_is_valid_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static int hi6220_clkdiv_bestdiv(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate) +{ + unsigned int i, bestdiv = 0; + unsigned long parent_rate, best = 0, now, maxdiv; + + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + struct clk *clk_parent = __clk_get_parent(hw->clk); + + maxdiv = hi6220_get_table_maxdiv(dclk->table); + + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = (bestdiv == 0) ? 1 : bestdiv; + bestdiv = (bestdiv > maxdiv) ? maxdiv : bestdiv; + return bestdiv; + } + + /* + * The maximum divider we can use without overflowing + * unsigned long in rate * i below + */ + maxdiv = min(ULONG_MAX / rate, maxdiv); + + for (i = 1; i <= maxdiv; i++) { + if (!hi6220_is_valid_div(dclk->table, i)) + continue; + parent_rate = __clk_round_rate(clk_parent, + MULT_ROUND_UP(rate, i)); + now = parent_rate / i; + if (now <= rate && now > best) { + bestdiv = i; + best = now; + *best_parent_rate = parent_rate; + } + } + + if (!bestdiv) { + bestdiv = hi6220_get_table_maxdiv(dclk->table); + *best_parent_rate = __clk_round_rate(clk_parent, 1); + } + + return bestdiv; +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int div; + + if (!rate) + rate = 1; + + div = hi6220_clkdiv_bestdiv(hw, rate, prate); + + return *prate / div; +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + unsigned int div, value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + div = parent_rate / rate; + + if (!hi6220_is_valid_div(dclk->table, div)) + return -EINVAL; + + value = hi6220_get_table_val(dclk->table, div); + + if (value > div_mask(dclk->width)) + value = div_mask(dclk->width); + + 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 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(struct hi6220_clk_divider), GFP_KERNEL); + if (!div) { + pr_err("%s: could not allocate divider clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kzalloc(sizeof(struct clk_div_table) * (max_div + 1), GFP_KERNEL); + if (!table) { + kfree(div); + pr_err("%s: failed to alloc divider table!\n", __func__); + 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 | CLK_IS_BASIC; + 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/include/dt-bindings/clock/hi6220-clock.h b/include/dt-bindings/clock/hi6220-clock.h new file mode 100644 index 0000000..dba096b --- /dev/null +++ b/include/dt-bindings/clock/hi6220-clock.h @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#ifndef __DT_BINDINGS_CLOCK_HI6220_H +#define __DT_BINDINGS_CLOCK_HI6220_H + +/* clk in AO (always on) controller */ +#define HI6220_NONE_CLOCK 0 + +/* fixed rate clocks */ +#define HI6220_REF32K 1 +#define HI6220_CLK_TCXO 2 +#define HI6220_MMC1_PAD 3 +#define HI6220_MMC2_PAD 4 +#define HI6220_MMC0_PAD 5 +#define HI6220_PLL_BBP 6 +#define HI6220_PLL_GPU 7 +#define HI6220_PLL1_DDR 8 +#define HI6220_PLL_SYS 9 +#define HI6220_PLL_SYS_MEDIA 10 +#define HI6220_DDR_SRC 11 +#define HI6220_PLL_MEDIA 12 +#define HI6220_PLL_DDR 13 + +/* fixed factor clocks */ +#define HI6220_300M 16 +#define HI6220_150M 17 +#define HI6220_PICOPHY_SRC 18 +#define HI6220_MMC0_SRC_SEL 19 +#define HI6220_MMC1_SRC_SEL 20 +#define HI6220_MMC2_SRC_SEL 21 +#define HI6220_VPU_CODEC 22 +#define HI6220_MMC0_SMP 23 +#define HI6220_MMC1_SMP 24 +#define HI6220_MMC2_SMP 25 + +/* gate clocks */ +#define HI6220_WDT0_PCLK 28 +#define HI6220_WDT1_PCLK 29 +#define HI6220_WDT2_PCLK 30 +#define HI6220_TIMER0_PCLK 31 +#define HI6220_TIMER1_PCLK 32 +#define HI6220_TIMER2_PCLK 33 +#define HI6220_TIMER3_PCLK 34 +#define HI6220_TIMER4_PCLK 35 +#define HI6220_TIMER5_PCLK 36 +#define HI6220_TIMER6_PCLK 37 +#define HI6220_TIMER7_PCLK 38 +#define HI6220_TIMER8_PCLK 39 +#define HI6220_UART0_PCLK 40 + +#define HI6220_AO_NR_CLKS 48 + +/* clk in systrl */ +/* gate clock */ +#define HI6220_MMC0_CLK 1 +#define HI6220_MMC0_CIUCLK 2 +#define HI6220_MMC1_CLK 3 +#define HI6220_MMC1_CIUCLK 4 +#define HI6220_MMC2_CLK 5 +#define HI6220_MMC2_CIUCLK 6 +#define HI6220_USBOTG_HCLK 7 +#define HI6220_CLK_PICOPHY 8 +#define HI6220_HIFI 9 +#define HI6220_DACODEC_PCLK 10 +#define HI6220_EDMAC_ACLK 11 +#define HI6220_CS_ATB 12 +#define HI6220_I2C0_CLK 13 +#define HI6220_I2C1_CLK 14 +#define HI6220_I2C2_CLK 15 +#define HI6220_I2C3_CLK 16 +#define HI6220_UART1_PCLK 17 +#define HI6220_UART2_PCLK 18 +#define HI6220_UART3_PCLK 19 +#define HI6220_UART4_PCLK 20 +#define HI6220_SPI_CLK 21 +#define HI6220_TSENSOR_CLK 22 +#define HI6220_MMU_CLK 23 +#define HI6220_HIFI_SEL 24 +#define HI6220_MMC0_SYSPLL 25 +#define HI6220_MMC1_SYSPLL 26 +#define HI6220_MMC2_SYSPLL 27 +#define HI6220_MMC0_SEL 28 +#define HI6220_MMC1_SEL 29 +#define HI6220_BBPPLL_SEL 30 +#define HI6220_MEDIA_PLL_SRC 31 +#define HI6220_MMC2_SEL 32 +#define HI6220_CS_ATB_SYSPLL 33 + +/* mux clocks */ +#define HI6220_MMC0_SRC 35 +#define HI6220_MMC0_SMP_IN 36 +#define HI6220_MMC1_SRC 37 +#define HI6220_MMC1_SMP_IN 38 +#define HI6220_MMC2_SRC 39 +#define HI6220_MMC2_SMP_IN 40 +#define HI6220_HIFI_SRC 41 +#define HI6220_UART1_SRC 42 +#define HI6220_UART2_SRC 43 +#define HI6220_UART3_SRC 44 +#define HI6220_UART4_SRC 45 +#define HI6220_MMC0_MUX0 46 +#define HI6220_MMC1_MUX0 47 +#define HI6220_MMC2_MUX0 48 +#define HI6220_MMC0_MUX1 49 +#define HI6220_MMC1_MUX1 50 +#define HI6220_MMC2_MUX1 51 + +/* divider clocks */ +#define HI6220_CLK_BUS 54 +#define HI6220_MMC0_DIV 55 +#define HI6220_MMC1_DIV 56 +#define HI6220_MMC2_DIV 57 +#define HI6220_HIFI_DIV 58 +#define HI6220_BBPPLL0_DIV 59 +#define HI6220_CS_DAPB 60 +#define HI6220_CS_ATB_DIV 61 + +#define HI6220_SYS_NR_CLKS 64 + +/* clk in media controller */ +/* gate clocks */ +#define HI6220_DSI_PCLK 1 +#define HI6220_G3D_PCLK 2 +#define HI6220_ACLK_CODEC_VPU 3 +#define HI6220_ISP_SCLK 4 +#define HI6220_ADE_CORE 5 +#define HI6220_MED_MMU 6 +#define HI6220_CFG_CSI4PHY 7 +#define HI6220_CFG_CSI2PHY 8 +#define HI6220_ISP_SCLK_GATE 9 +#define HI6220_ISP_SCLK_GATE1 10 +#define HI6220_ADE_CORE_GATE 11 +#define HI6220_CODEC_VPU_GATE 12 +#define HI6220_MED_SYSPLL 13 + +/* mux clocks */ +#define HI6220_1440_1200 20 +#define HI6220_1000_1200 21 +#define HI6220_1000_1440 22 + +/* divider clocks */ +#define HI6220_CODEC_JPEG 30 +#define HI6220_ISP_SCLK_SRC 31 +#define HI6220_ISP_SCLK1 32 +#define HI6220_ADE_CORE_SRC 33 +#define HI6220_ADE_PIX_SRC 34 +#define HI6220_G3D_CLK 35 +#define HI6220_CODEC_VPU_SRC 36 + +#define HI6220_MEDIA_NR_CLKS 40 + +/* clk in power controller */ +/* gate clocks */ +#define HI6220_PLL_GPU_GATE 1 +#define HI6220_PLL1_DDR_GATE 2 +#define HI6220_PLL_DDR_GATE 3 +#define HI6220_PLL_MEDIA_GATE 4 +#define HI6220_PLL0_BBP_GATE 5 + +/* divider clocks */ +#define HI6220_DDRC_SRC 10 +#define HI6220_DDRC_AXI1 11 + +#define HI6220_POWER_NR_CLKS 16 +#endif