diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 18:04:57 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 18:04:57 (GMT) |
commit | 5617c122e6015e2371c0bd6b5ad2e070844df24a (patch) | |
tree | 53082aa1d585d8a9453d1f121b91bc86da1587bc /drivers | |
parent | 77b0a4aa0732f1856aef85b8db085864e5971a14 (diff) | |
parent | b4626a7f489238a59f08f0b216e883bac07260d7 (diff) | |
download | linux-5617c122e6015e2371c0bd6b5ad2e070844df24a.tar.xz |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk framework updates from Stephen Boyd:
"The core clk framework changes are small again. They're mostly minor
fixes that weren't causing enough problems (or any problems when we're
just clarifying things) to warrant sending outside the merge window.
The majority of changes are in drivers for various SoCs. Full details
are in the logs, but here's the summary.
Core:
- Better support for DeviceTree overlays with the addition of the
CLK_OF_DECLARE_DRIVER macro. Now we won't probe a clk driver for a
device node that matched during of_clk_init(), unless the driver
uses CLK_OF_DECLARE_DRIVER instead of CLK_OF_DECLARE. This allows
overlays to work cleanly for drivers that must probe before the
device model is ready, and also after it's ready when an overlay is
loaded.
- Clarification in the code around how clk_hw pointers are returned
from of clk providers
- Proper migration of prepare/enable counts to parents when the clk
tree is constructed
New Drivers:
- Socionext's UniPhier SoCs
- Loongson1C
- ZTE ZX296718
- Qualcomm MDM9615
- Amlogic GXBB AO clocks and resets
- Broadcom BCM53573 ILP
- Maxim MAX77620
Updates:
- Four Allwinner SoCs are migrated to the new style clk driver (A31,
A31s, A23 and A33)
- Exynos 5xxx audio and DRAM clks
- Loongson1B AC97, DMA and NAND clks
- Rockchip DDR clks and rk3399 driver tweaks
- Renesas R-Car M3-W (r8a7796) SoC SDHI interface and Watchdog timer
clks
- Renasas R-Car H3 and M3-W CMT clks and RAVB+Thermal clks for M3-W
- Amlogic GXBB MMC gate clks
- at91 sama5d4 sckc
- Removal of STiH415 and STiH416 clk support as the SoC is being
removed
- Rework of STiH4xx clk support for new style bindings
- Continuation of driver migration to clk_hw based registration APIs
- xgene PMD support
- bcm2835 critical clk markings
- ARM versatile ICST"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (199 commits)
CLK: Add Loongson1C clock support
clk: Loongson1: Make use of GENMASK
clk: Loongson1: Update clocks of Loongson1B
clk: Loongson1: Refactor Loongson1 clock
clk: change the type of clk_hw_onecell_data.num to unsigned int
clk: zx296718: register driver earlier with core_initcall
clk: mvebu: dynamically allocate resources in Armada CP110 system controller
clk: mvebu: fix setting unwanted flags in CP110 gate clock
clk: nxp: clk-lpc32xx: Unmap region obtained by of_iomap
clk: mediatek: clk-mt8173: Unmap region obtained by of_iomap
clk: sunxi-ng: Fix reset offset for the A23 and A33
clk: at91: sckc: optimize boot time
clk: at91: Add sama5d4 sckc support
clk: at91: move slow clock controller clocks to sckc.c
clk: imx6: initialize GPU clocks
clk: imx6: fix i.MX6DL clock tree to reflect reality
clk: imx53: Add clocks configuration
clk: uniphier: add clock data for UniPhier SoCs
clk: uniphier: add core support code for UniPhier clock driver
clk: bcm: Add driver for BCM53573 ILP clock
...
Diffstat (limited to 'drivers')
172 files changed, 13067 insertions, 3873 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e2d9bd7..6a8ac04 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -31,22 +31,12 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" -config COMMON_CLK_MAX_GEN - bool - config COMMON_CLK_MAX77686 - tristate "Clock driver for Maxim 77686 MFD" - depends on MFD_MAX77686 - select COMMON_CLK_MAX_GEN - ---help--- - This driver supports Maxim 77686 crystal oscillator clock. - -config COMMON_CLK_MAX77802 - tristate "Clock driver for Maxim 77802 PMIC" - depends on MFD_MAX77686 - select COMMON_CLK_MAX_GEN + tristate "Clock driver for Maxim 77620/77686/77802 MFD" + depends on MFD_MAX77686 || MFD_MAX77620 ---help--- - This driver supports Maxim 77802 crystal oscillator clock. + This driver supports Maxim 77620/77686/77802 crystal oscillator + clock. config COMMON_CLK_RK808 tristate "Clock driver for RK808/RK818" @@ -210,6 +200,7 @@ config COMMON_CLK_OXNAS source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" +source "drivers/clk/mediatek/Kconfig" source "drivers/clk/meson/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/qcom/Kconfig" @@ -218,5 +209,6 @@ source "drivers/clk/samsung/Kconfig" source "drivers/clk/sunxi-ng/Kconfig" source "drivers/clk/tegra/Kconfig" source "drivers/clk/ti/Kconfig" +source "drivers/clk/uniphier/Kconfig" endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3b6f9cf..925081e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -26,10 +26,7 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o -obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o -obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o -obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o @@ -63,6 +60,7 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_MACH_INGENIC) += ingenic/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_MACH_LOONGSON32) += loongson1/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/ obj-$(CONFIG_MACH_PIC32) += microchip/ @@ -86,6 +84,7 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += ti/ +obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index 7f6bec8..4e1cd5a 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -233,14 +233,16 @@ static void clk_generated_startup(struct clk_generated *gck) >> AT91_PMC_PCR_GCKDIV_OFFSET; } -static struct clk * __init -at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const char - *name, const char **parent_names, u8 num_parents, - u8 id, const struct clk_range *range) +static struct clk_hw * __init +at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, + const char *name, const char **parent_names, + u8 num_parents, u8 id, + const struct clk_range *range) { struct clk_generated *gck; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; gck = kzalloc(sizeof(*gck), GFP_KERNEL); if (!gck) @@ -258,13 +260,15 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const char gck->lock = lock; gck->range = *range; - clk = clk_register(NULL, &gck->hw); - if (IS_ERR(clk)) + hw = &gck->hw; + ret = clk_hw_register(NULL, &gck->hw); + if (ret) { kfree(gck); - else + hw = ERR_PTR(ret); + } else clk_generated_startup(gck); - return clk; + return hw; } static void __init of_sama5d2_clk_generated_setup(struct device_node *np) @@ -272,7 +276,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) int num; u32 id; const char *name; - struct clk *clk; + struct clk_hw *hw; unsigned int num_parents; const char *parent_names[GENERATED_SOURCE_MAX]; struct device_node *gcknp; @@ -306,13 +310,13 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) of_at91_get_clk_range(gcknp, "atmel,clk-output-range", &range); - clk = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, parent_names, num_parents, id, &range); - if (IS_ERR(clk)) + if (IS_ERR(hw)) continue; - of_clk_add_provider(gcknp, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw); } } CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated", diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c index 8e20c8a..e0daa4a 100644 --- a/drivers/clk/at91/clk-h32mx.c +++ b/drivers/clk/at91/clk-h32mx.c @@ -92,7 +92,7 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np) struct clk_init_data init; const char *parent_name; struct regmap *regmap; - struct clk *clk; + int ret; regmap = syscon_node_to_regmap(of_get_parent(np)); if (IS_ERR(regmap)) @@ -113,13 +113,13 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np) h32mxclk->hw.init = &init; h32mxclk->regmap = regmap; - clk = clk_register(NULL, &h32mxclk->hw); - if (IS_ERR(clk)) { + ret = clk_hw_register(NULL, &h32mxclk->hw); + if (ret) { kfree(h32mxclk); return; } - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw); } CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx", of_sama5d4_clk_h32mx_setup); diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index 58b5bac..c813c27 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -128,15 +128,16 @@ static const struct clk_ops main_osc_ops = { .is_prepared = clk_main_osc_is_prepared, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_main_osc(struct regmap *regmap, const char *name, const char *parent_name, bool bypass) { struct clk_main_osc *osc; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !parent_name) return ERR_PTR(-EINVAL); @@ -160,16 +161,19 @@ at91_clk_register_main_osc(struct regmap *regmap, AT91_PMC_MOSCEN, AT91_PMC_OSCBYPASS | AT91_PMC_KEY); - clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); + if (ret) { kfree(osc); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *name = np->name; const char *parent_name; struct regmap *regmap; @@ -183,11 +187,11 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass); - if (IS_ERR(clk)) + hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc", of_at91rm9200_clk_main_osc_setup); @@ -271,14 +275,15 @@ static const struct clk_ops main_rc_osc_ops = { .recalc_accuracy = clk_main_rc_osc_recalc_accuracy, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name, u32 frequency, u32 accuracy) { struct clk_main_rc_osc *osc; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !frequency) return ERR_PTR(-EINVAL); @@ -298,16 +303,19 @@ at91_clk_register_main_rc_osc(struct regmap *regmap, osc->frequency = frequency; osc->accuracy = accuracy; - clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) + hw = &osc->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { kfree(osc); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; u32 frequency = 0; u32 accuracy = 0; const char *name = np->name; @@ -321,11 +329,11 @@ static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy); - if (IS_ERR(clk)) + hw = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc", of_at91sam9x5_clk_main_rc_osc_setup); @@ -395,14 +403,15 @@ static const struct clk_ops rm9200_main_ops = { .recalc_rate = clk_rm9200_main_recalc_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_rm9200_main(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_rm9200_main *clkmain; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -423,16 +432,19 @@ at91_clk_register_rm9200_main(struct regmap *regmap, clkmain->hw.init = &init; clkmain->regmap = regmap; - clk = clk_register(NULL, &clkmain->hw); - if (IS_ERR(clk)) + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); + if (ret) { kfree(clkmain); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91rm9200_clk_main_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name = np->name; struct regmap *regmap; @@ -444,11 +456,11 @@ static void __init of_at91rm9200_clk_main_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91_clk_register_rm9200_main(regmap, name, parent_name); - if (IS_ERR(clk)) + hw = at91_clk_register_rm9200_main(regmap, name, parent_name); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main", of_at91rm9200_clk_main_setup); @@ -529,16 +541,17 @@ static const struct clk_ops sam9x5_main_ops = { .get_parent = clk_sam9x5_main_get_parent, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name, const char **parent_names, int num_parents) { struct clk_sam9x5_main *clkmain; - struct clk *clk = NULL; struct clk_init_data init; unsigned int status; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -561,16 +574,19 @@ at91_clk_register_sam9x5_main(struct regmap *regmap, regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0; - clk = clk_register(NULL, &clkmain->hw); - if (IS_ERR(clk)) + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); + if (ret) { kfree(clkmain); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_main_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_names[2]; unsigned int num_parents; const char *name = np->name; @@ -587,12 +603,12 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np) of_property_read_string(np, "clock-output-names", &name); - clk = at91_clk_register_sam9x5_main(regmap, name, parent_names, + hw = at91_clk_register_sam9x5_main(regmap, name, parent_names, num_parents); - if (IS_ERR(clk)) + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main", of_at91sam9x5_clk_main_setup); diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index d1021e1..e9cba9f 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -120,7 +120,7 @@ static const struct clk_ops master_ops = { .get_parent = clk_master_get_parent, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_master(struct regmap *regmap, const char *name, int num_parents, const char **parent_names, @@ -128,8 +128,9 @@ at91_clk_register_master(struct regmap *regmap, const struct clk_master_characteristics *characteristics) { struct clk_master *master; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !num_parents || !parent_names) return ERR_PTR(-EINVAL); @@ -149,12 +150,14 @@ at91_clk_register_master(struct regmap *regmap, master->characteristics = characteristics; master->regmap = regmap; - clk = clk_register(NULL, &master->hw); - if (IS_ERR(clk)) { + hw = &master->hw; + ret = clk_hw_register(NULL, &master->hw); + if (ret) { kfree(master); + hw = ERR_PTR(ret); } - return clk; + return hw; } @@ -198,7 +201,7 @@ static void __init of_at91_clk_master_setup(struct device_node *np, const struct clk_master_layout *layout) { - struct clk *clk; + struct clk_hw *hw; unsigned int num_parents; const char *parent_names[MASTER_SOURCE_MAX]; const char *name = np->name; @@ -221,13 +224,13 @@ of_at91_clk_master_setup(struct device_node *np, if (IS_ERR(regmap)) return; - clk = at91_clk_register_master(regmap, name, num_parents, + hw = at91_clk_register_master(regmap, name, num_parents, parent_names, layout, characteristics); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto out_free_characteristics; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); return; out_free_characteristics: diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index fd16072..dc29fd9 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -104,13 +104,14 @@ static const struct clk_ops peripheral_ops = { .is_enabled = clk_peripheral_is_enabled, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_peripheral(struct regmap *regmap, const char *name, const char *parent_name, u32 id) { struct clk_peripheral *periph; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !parent_name || id > PERIPHERAL_ID_MAX) return ERR_PTR(-EINVAL); @@ -129,11 +130,14 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name, periph->hw.init = &init; periph->regmap = regmap; - clk = clk_register(NULL, &periph->hw); - if (IS_ERR(clk)) + hw = &periph->hw; + ret = clk_hw_register(NULL, &periph->hw); + if (ret) { kfree(periph); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) @@ -327,14 +331,15 @@ static const struct clk_ops sam9x5_peripheral_ops = { .set_rate = clk_sam9x5_peripheral_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, u32 id, const struct clk_range *range) { struct clk_sam9x5_peripheral *periph; - struct clk *clk = NULL; struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !parent_name) return ERR_PTR(-EINVAL); @@ -357,13 +362,15 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, periph->auto_div = true; periph->range = *range; - clk = clk_register(NULL, &periph->hw); - if (IS_ERR(clk)) + hw = &periph->hw; + ret = clk_hw_register(NULL, &periph->hw); + if (ret) { kfree(periph); - else + hw = ERR_PTR(ret); + } else clk_sam9x5_peripheral_autodiv(periph); - return clk; + return hw; } static void __init @@ -371,7 +378,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type) { int num; u32 id; - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name; struct device_node *periphclknp; @@ -400,7 +407,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type) name = periphclknp->name; if (type == PERIPHERAL_AT91RM9200) { - clk = at91_clk_register_peripheral(regmap, name, + hw = at91_clk_register_peripheral(regmap, name, parent_name, id); } else { struct clk_range range = CLK_RANGE(0, 0); @@ -409,17 +416,17 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type) "atmel,clk-output-range", &range); - clk = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, name, parent_name, id, &range); } - if (IS_ERR(clk)) + if (IS_ERR(hw)) continue; - of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw); } } diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index fb2e0b5..45ad168 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -296,17 +296,18 @@ static const struct clk_ops pll_ops = { .set_rate = clk_pll_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_pll(struct regmap *regmap, const char *name, const char *parent_name, u8 id, const struct clk_pll_layout *layout, const struct clk_pll_characteristics *characteristics) { struct clk_pll *pll; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; int offset = PLL_REG(id); unsigned int pllr; + int ret; if (id > PLL_MAX_ID) return ERR_PTR(-EINVAL); @@ -330,12 +331,14 @@ at91_clk_register_pll(struct regmap *regmap, const char *name, pll->div = PLL_DIV(pllr); pll->mul = PLL_MUL(pllr, layout); - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) { + hw = &pll->hw; + ret = clk_hw_register(NULL, &pll->hw); + if (ret) { kfree(pll); + hw = ERR_PTR(ret); } - return clk; + return hw; } @@ -465,7 +468,7 @@ of_at91_clk_pll_setup(struct device_node *np, const struct clk_pll_layout *layout) { u32 id; - struct clk *clk; + struct clk_hw *hw; struct regmap *regmap; const char *parent_name; const char *name = np->name; @@ -486,12 +489,12 @@ of_at91_clk_pll_setup(struct device_node *np, if (!characteristics) return; - clk = at91_clk_register_pll(regmap, name, parent_name, id, layout, + hw = at91_clk_register_pll(regmap, name, parent_name, id, layout, characteristics); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto out_free_characteristics; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); return; out_free_characteristics: diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c index 2bed264..b4afaf2 100644 --- a/drivers/clk/at91/clk-plldiv.c +++ b/drivers/clk/at91/clk-plldiv.c @@ -75,13 +75,14 @@ static const struct clk_ops plldiv_ops = { .set_rate = clk_plldiv_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_plldiv(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_plldiv *plldiv; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL); if (!plldiv) @@ -96,18 +97,20 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name, plldiv->hw.init = &init; plldiv->regmap = regmap; - clk = clk_register(NULL, &plldiv->hw); - - if (IS_ERR(clk)) + hw = &plldiv->hw; + ret = clk_hw_register(NULL, &plldiv->hw); + if (ret) { kfree(plldiv); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name = np->name; struct regmap *regmap; @@ -120,12 +123,11 @@ of_at91sam9x5_clk_plldiv_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91_clk_register_plldiv(regmap, name, parent_name); - if (IS_ERR(clk)) + hw = at91_clk_register_plldiv(regmap, name, parent_name); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); - return; + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv", of_at91sam9x5_clk_plldiv_setup); diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 25d5906..190122e64 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -170,15 +170,16 @@ static const struct clk_ops programmable_ops = { .set_rate = clk_programmable_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_programmable(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents, u8 id, const struct clk_programmable_layout *layout) { struct clk_programmable *prog; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (id > PROG_ID_MAX) return ERR_PTR(-EINVAL); @@ -198,11 +199,14 @@ at91_clk_register_programmable(struct regmap *regmap, prog->hw.init = &init; prog->regmap = regmap; - clk = clk_register(NULL, &prog->hw); - if (IS_ERR(clk)) + hw = &prog->hw; + ret = clk_hw_register(NULL, &prog->hw); + if (ret) { kfree(prog); + hw = &prog->hw; + } - return clk; + return hw; } static const struct clk_programmable_layout at91rm9200_programmable_layout = { @@ -229,7 +233,7 @@ of_at91_clk_prog_setup(struct device_node *np, { int num; u32 id; - struct clk *clk; + struct clk_hw *hw; unsigned int num_parents; const char *parent_names[PROG_SOURCE_MAX]; const char *name; @@ -257,13 +261,13 @@ of_at91_clk_prog_setup(struct device_node *np, if (of_property_read_string(np, "clock-output-names", &name)) name = progclknp->name; - clk = at91_clk_register_programmable(regmap, name, + hw = at91_clk_register_programmable(regmap, name, parent_names, num_parents, id, layout); - if (IS_ERR(clk)) + if (IS_ERR(hw)) continue; - of_clk_add_provider(progclknp, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(progclknp, of_clk_hw_simple_get, hw); } } diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 61090b1..560a8b9 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -13,42 +13,11 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include "pmc.h" -#include "sckc.h" - -#define SLOW_CLOCK_FREQ 32768 -#define SLOWCK_SW_CYCLES 5 -#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ - SLOW_CLOCK_FREQ) - -#define AT91_SCKC_CR 0x00 -#define AT91_SCKC_RCEN (1 << 0) -#define AT91_SCKC_OSC32EN (1 << 1) -#define AT91_SCKC_OSC32BYP (1 << 2) -#define AT91_SCKC_OSCSEL (1 << 3) - -struct clk_slow_osc { - struct clk_hw hw; - void __iomem *sckcr; - unsigned long startup_usec; -}; - -#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) - -struct clk_slow_rc_osc { - struct clk_hw hw; - void __iomem *sckcr; - unsigned long frequency; - unsigned long accuracy; - unsigned long startup_usec; -}; - -#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) struct clk_sam9260_slow { struct clk_hw hw; @@ -57,328 +26,6 @@ struct clk_sam9260_slow { #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) -struct clk_sam9x5_slow { - struct clk_hw hw; - void __iomem *sckcr; - u8 parent; -}; - -#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) - -static int clk_slow_osc_prepare(struct clk_hw *hw) -{ - struct clk_slow_osc *osc = to_clk_slow_osc(hw); - void __iomem *sckcr = osc->sckcr; - u32 tmp = readl(sckcr); - - if (tmp & AT91_SCKC_OSC32BYP) - return 0; - - writel(tmp | AT91_SCKC_OSC32EN, sckcr); - - usleep_range(osc->startup_usec, osc->startup_usec + 1); - - return 0; -} - -static void clk_slow_osc_unprepare(struct clk_hw *hw) -{ - struct clk_slow_osc *osc = to_clk_slow_osc(hw); - void __iomem *sckcr = osc->sckcr; - u32 tmp = readl(sckcr); - - if (tmp & AT91_SCKC_OSC32BYP) - return; - - writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); -} - -static int clk_slow_osc_is_prepared(struct clk_hw *hw) -{ - struct clk_slow_osc *osc = to_clk_slow_osc(hw); - void __iomem *sckcr = osc->sckcr; - u32 tmp = readl(sckcr); - - if (tmp & AT91_SCKC_OSC32BYP) - return 1; - - return !!(tmp & AT91_SCKC_OSC32EN); -} - -static const struct clk_ops slow_osc_ops = { - .prepare = clk_slow_osc_prepare, - .unprepare = clk_slow_osc_unprepare, - .is_prepared = clk_slow_osc_is_prepared, -}; - -static struct clk * __init -at91_clk_register_slow_osc(void __iomem *sckcr, - const char *name, - const char *parent_name, - unsigned long startup, - bool bypass) -{ - struct clk_slow_osc *osc; - struct clk *clk = NULL; - struct clk_init_data init; - - if (!sckcr || !name || !parent_name) - return ERR_PTR(-EINVAL); - - osc = kzalloc(sizeof(*osc), GFP_KERNEL); - if (!osc) - return ERR_PTR(-ENOMEM); - - init.name = name; - init.ops = &slow_osc_ops; - init.parent_names = &parent_name; - init.num_parents = 1; - init.flags = CLK_IGNORE_UNUSED; - - osc->hw.init = &init; - osc->sckcr = sckcr; - osc->startup_usec = startup; - - if (bypass) - writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, - sckcr); - - clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) - kfree(osc); - - return clk; -} - -void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, - void __iomem *sckcr) -{ - struct clk *clk; - const char *parent_name; - const char *name = np->name; - u32 startup; - bool bypass; - - parent_name = of_clk_get_parent_name(np, 0); - of_property_read_string(np, "clock-output-names", &name); - of_property_read_u32(np, "atmel,startup-time-usec", &startup); - bypass = of_property_read_bool(np, "atmel,osc-bypass"); - - clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, - bypass); - if (IS_ERR(clk)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, clk); -} - -static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - - return osc->frequency; -} - -static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, - unsigned long parent_acc) -{ - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - - return osc->accuracy; -} - -static int clk_slow_rc_osc_prepare(struct clk_hw *hw) -{ - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - void __iomem *sckcr = osc->sckcr; - - writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); - - usleep_range(osc->startup_usec, osc->startup_usec + 1); - - return 0; -} - -static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) -{ - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - void __iomem *sckcr = osc->sckcr; - - writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); -} - -static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) -{ - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - - return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); -} - -static const struct clk_ops slow_rc_osc_ops = { - .prepare = clk_slow_rc_osc_prepare, - .unprepare = clk_slow_rc_osc_unprepare, - .is_prepared = clk_slow_rc_osc_is_prepared, - .recalc_rate = clk_slow_rc_osc_recalc_rate, - .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, -}; - -static struct clk * __init -at91_clk_register_slow_rc_osc(void __iomem *sckcr, - const char *name, - unsigned long frequency, - unsigned long accuracy, - unsigned long startup) -{ - struct clk_slow_rc_osc *osc; - struct clk *clk = NULL; - struct clk_init_data init; - - if (!sckcr || !name) - return ERR_PTR(-EINVAL); - - osc = kzalloc(sizeof(*osc), GFP_KERNEL); - if (!osc) - return ERR_PTR(-ENOMEM); - - init.name = name; - init.ops = &slow_rc_osc_ops; - init.parent_names = NULL; - init.num_parents = 0; - init.flags = CLK_IGNORE_UNUSED; - - osc->hw.init = &init; - osc->sckcr = sckcr; - osc->frequency = frequency; - osc->accuracy = accuracy; - osc->startup_usec = startup; - - clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) - kfree(osc); - - return clk; -} - -void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, - void __iomem *sckcr) -{ - struct clk *clk; - u32 frequency = 0; - u32 accuracy = 0; - u32 startup = 0; - const char *name = np->name; - - of_property_read_string(np, "clock-output-names", &name); - of_property_read_u32(np, "clock-frequency", &frequency); - of_property_read_u32(np, "clock-accuracy", &accuracy); - of_property_read_u32(np, "atmel,startup-time-usec", &startup); - - clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, - startup); - if (IS_ERR(clk)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, clk); -} - -static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); - void __iomem *sckcr = slowck->sckcr; - u32 tmp; - - if (index > 1) - return -EINVAL; - - tmp = readl(sckcr); - - if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || - (index && (tmp & AT91_SCKC_OSCSEL))) - return 0; - - if (index) - tmp |= AT91_SCKC_OSCSEL; - else - tmp &= ~AT91_SCKC_OSCSEL; - - writel(tmp, sckcr); - - usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); - - return 0; -} - -static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) -{ - struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); - - return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); -} - -static const struct clk_ops sam9x5_slow_ops = { - .set_parent = clk_sam9x5_slow_set_parent, - .get_parent = clk_sam9x5_slow_get_parent, -}; - -static struct clk * __init -at91_clk_register_sam9x5_slow(void __iomem *sckcr, - const char *name, - const char **parent_names, - int num_parents) -{ - struct clk_sam9x5_slow *slowck; - struct clk *clk = NULL; - struct clk_init_data init; - - if (!sckcr || !name || !parent_names || !num_parents) - return ERR_PTR(-EINVAL); - - slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); - if (!slowck) - return ERR_PTR(-ENOMEM); - - init.name = name; - init.ops = &sam9x5_slow_ops; - init.parent_names = parent_names; - init.num_parents = num_parents; - init.flags = 0; - - slowck->hw.init = &init; - slowck->sckcr = sckcr; - slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); - - clk = clk_register(NULL, &slowck->hw); - if (IS_ERR(clk)) - kfree(slowck); - - return clk; -} - -void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, - void __iomem *sckcr) -{ - struct clk *clk; - const char *parent_names[2]; - unsigned int num_parents; - const char *name = np->name; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > 2) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - - of_property_read_string(np, "clock-output-names", &name); - - clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, - num_parents); - if (IS_ERR(clk)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, clk); -} - static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) { struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); @@ -393,15 +40,16 @@ static const struct clk_ops sam9260_slow_ops = { .get_parent = clk_sam9260_slow_get_parent, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_sam9260_slow(struct regmap *regmap, const char *name, const char **parent_names, int num_parents) { struct clk_sam9260_slow *slowck; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -422,16 +70,19 @@ at91_clk_register_sam9260_slow(struct regmap *regmap, slowck->hw.init = &init; slowck->regmap = regmap; - clk = clk_register(NULL, &slowck->hw); - if (IS_ERR(clk)) + hw = &slowck->hw; + ret = clk_hw_register(NULL, &slowck->hw); + if (ret) { kfree(slowck); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9260_clk_slow_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_names[2]; unsigned int num_parents; const char *name = np->name; @@ -448,12 +99,12 @@ static void __init of_at91sam9260_clk_slow_setup(struct device_node *np) of_property_read_string(np, "clock-output-names", &name); - clk = at91_clk_register_sam9260_slow(regmap, name, parent_names, + hw = at91_clk_register_sam9260_slow(regmap, name, parent_names, num_parents); - if (IS_ERR(clk)) + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow", diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c index 3c04b06..965c662 100644 --- a/drivers/clk/at91/clk-smd.c +++ b/drivers/clk/at91/clk-smd.c @@ -111,13 +111,14 @@ static const struct clk_ops at91sam9x5_smd_ops = { .set_rate = at91sam9x5_clk_smd_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents) { struct at91sam9x5_clk_smd *smd; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; smd = kzalloc(sizeof(*smd), GFP_KERNEL); if (!smd) @@ -132,16 +133,19 @@ at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name, smd->hw.init = &init; smd->regmap = regmap; - clk = clk_register(NULL, &smd->hw); - if (IS_ERR(clk)) + hw = &smd->hw; + ret = clk_hw_register(NULL, &smd->hw); + if (ret) { kfree(smd); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; unsigned int num_parents; const char *parent_names[SMD_SOURCE_MAX]; const char *name = np->name; @@ -159,12 +163,12 @@ static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91sam9x5_clk_register_smd(regmap, name, parent_names, + hw = at91sam9x5_clk_register_smd(regmap, name, parent_names, num_parents); - if (IS_ERR(clk)) + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd", of_at91sam9x5_clk_smd_setup); diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 8f35d81..86a3680 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -88,13 +88,14 @@ static const struct clk_ops system_ops = { .is_prepared = clk_system_is_prepared, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_system(struct regmap *regmap, const char *name, const char *parent_name, u8 id) { struct clk_system *sys; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (!parent_name || id > SYSTEM_MAX_ID) return ERR_PTR(-EINVAL); @@ -113,18 +114,21 @@ at91_clk_register_system(struct regmap *regmap, const char *name, sys->hw.init = &init; sys->regmap = regmap; - clk = clk_register(NULL, &sys->hw); - if (IS_ERR(clk)) + hw = &sys->hw; + ret = clk_hw_register(NULL, &sys->hw); + if (ret) { kfree(sys); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91rm9200_clk_sys_setup(struct device_node *np) { int num; u32 id; - struct clk *clk; + struct clk_hw *hw; const char *name; struct device_node *sysclknp; const char *parent_name; @@ -147,11 +151,11 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np) parent_name = of_clk_get_parent_name(sysclknp, 0); - clk = at91_clk_register_system(regmap, name, parent_name, id); - if (IS_ERR(clk)) + hw = at91_clk_register_system(regmap, name, parent_name, id); + if (IS_ERR(hw)) continue; - of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(sysclknp, of_clk_hw_simple_get, hw); } } CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system", diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index d80bdb0..791770a 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -192,13 +192,14 @@ static const struct clk_ops at91sam9n12_usb_ops = { .set_rate = at91sam9x5_clk_usb_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents) { struct at91sam9x5_clk_usb *usb; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; usb = kzalloc(sizeof(*usb), GFP_KERNEL); if (!usb) @@ -214,20 +215,24 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, usb->hw.init = &init; usb->regmap = regmap; - clk = clk_register(NULL, &usb->hw); - if (IS_ERR(clk)) + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); + if (ret) { kfree(usb); + hw = ERR_PTR(ret); + } - return clk; + return hw; } -static struct clk * __init +static struct clk_hw * __init at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name) { struct at91sam9x5_clk_usb *usb; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; usb = kzalloc(sizeof(*usb), GFP_KERNEL); if (!usb) @@ -242,11 +247,14 @@ at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, usb->hw.init = &init; usb->regmap = regmap; - clk = clk_register(NULL, &usb->hw); - if (IS_ERR(clk)) + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); + if (ret) { kfree(usb); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw, @@ -334,13 +342,14 @@ static const struct clk_ops at91rm9200_usb_ops = { .set_rate = at91rm9200_clk_usb_set_rate, }; -static struct clk * __init +static struct clk_hw * __init at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name, const u32 *divisors) { struct at91rm9200_clk_usb *usb; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; usb = kzalloc(sizeof(*usb), GFP_KERNEL); if (!usb) @@ -356,16 +365,19 @@ at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, usb->regmap = regmap; memcpy(usb->divisors, divisors, sizeof(usb->divisors)); - clk = clk_register(NULL, &usb->hw); - if (IS_ERR(clk)) + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); + if (ret) { kfree(usb); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; unsigned int num_parents; const char *parent_names[USB_SOURCE_MAX]; const char *name = np->name; @@ -383,19 +395,19 @@ static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91sam9x5_clk_register_usb(regmap, name, parent_names, - num_parents); - if (IS_ERR(clk)) + hw = at91sam9x5_clk_register_usb(regmap, name, parent_names, + num_parents); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb", of_at91sam9x5_clk_usb_setup); static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name = np->name; struct regmap *regmap; @@ -410,18 +422,18 @@ static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91sam9n12_clk_register_usb(regmap, name, parent_name); - if (IS_ERR(clk)) + hw = at91sam9n12_clk_register_usb(regmap, name, parent_name); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb", of_at91sam9n12_clk_usb_setup); static void __init of_at91rm9200_clk_usb_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name = np->name; u32 divisors[4] = {0, 0, 0, 0}; @@ -440,12 +452,11 @@ static void __init of_at91rm9200_clk_usb_setup(struct device_node *np) regmap = syscon_node_to_regmap(of_get_parent(np)); if (IS_ERR(regmap)) return; - - clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors); - if (IS_ERR(clk)) + hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb", of_at91rm9200_clk_usb_setup); diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index 61fcf39..aadabd9 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -77,13 +77,14 @@ static const struct clk_ops utmi_ops = { .recalc_rate = clk_utmi_recalc_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_utmi(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_utmi *utmi; - struct clk *clk = NULL; + struct clk_hw *hw; struct clk_init_data init; + int ret; utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); if (!utmi) @@ -98,16 +99,19 @@ at91_clk_register_utmi(struct regmap *regmap, utmi->hw.init = &init; utmi->regmap = regmap; - clk = clk_register(NULL, &utmi->hw); - if (IS_ERR(clk)) + hw = &utmi->hw; + ret = clk_hw_register(NULL, &utmi->hw); + if (ret) { kfree(utmi); + hw = ERR_PTR(ret); + } - return clk; + return hw; } static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; const char *parent_name; const char *name = np->name; struct regmap *regmap; @@ -120,11 +124,11 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) if (IS_ERR(regmap)) return; - clk = at91_clk_register_utmi(regmap, name, parent_name); - if (IS_ERR(clk)) + hw = at91_clk_register_utmi(regmap, name, parent_name); + if (IS_ERR(hw)) return; - of_clk_add_provider(np, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); return; } CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi", diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c index 1184d76..ab6ecef 100644 --- a/drivers/clk/at91/sckc.c +++ b/drivers/clk/at91/sckc.c @@ -12,11 +12,382 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/delay.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/io.h> -#include "sckc.h" +#define SLOW_CLOCK_FREQ 32768 +#define SLOWCK_SW_CYCLES 5 +#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ + SLOW_CLOCK_FREQ) + +#define AT91_SCKC_CR 0x00 +#define AT91_SCKC_RCEN (1 << 0) +#define AT91_SCKC_OSC32EN (1 << 1) +#define AT91_SCKC_OSC32BYP (1 << 2) +#define AT91_SCKC_OSCSEL (1 << 3) + +struct clk_slow_osc { + struct clk_hw hw; + void __iomem *sckcr; + unsigned long startup_usec; +}; + +#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) + +struct clk_sama5d4_slow_osc { + struct clk_hw hw; + void __iomem *sckcr; + unsigned long startup_usec; + bool prepared; +}; + +#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) + +struct clk_slow_rc_osc { + struct clk_hw hw; + void __iomem *sckcr; + unsigned long frequency; + unsigned long accuracy; + unsigned long startup_usec; +}; + +#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) + +struct clk_sam9x5_slow { + struct clk_hw hw; + void __iomem *sckcr; + u8 parent; +}; + +#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) + +static int clk_slow_osc_prepare(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN)) + return 0; + + writel(tmp | AT91_SCKC_OSC32EN, sckcr); + + usleep_range(osc->startup_usec, osc->startup_usec + 1); + + return 0; +} + +static void clk_slow_osc_unprepare(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return; + + writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); +} + +static int clk_slow_osc_is_prepared(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return 1; + + return !!(tmp & AT91_SCKC_OSC32EN); +} + +static const struct clk_ops slow_osc_ops = { + .prepare = clk_slow_osc_prepare, + .unprepare = clk_slow_osc_unprepare, + .is_prepared = clk_slow_osc_is_prepared, +}; + +static struct clk_hw * __init +at91_clk_register_slow_osc(void __iomem *sckcr, + const char *name, + const char *parent_name, + unsigned long startup, + bool bypass) +{ + struct clk_slow_osc *osc; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (!sckcr || !name || !parent_name) + return ERR_PTR(-EINVAL); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &slow_osc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + + osc->hw.init = &init; + osc->sckcr = sckcr; + osc->startup_usec = startup; + + if (bypass) + writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, + sckcr); + + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); + if (ret) { + kfree(osc); + hw = ERR_PTR(ret); + } + + return hw; +} + +static void __init +of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk_hw *hw; + const char *parent_name; + const char *name = np->name; + u32 startup; + bool bypass; + + parent_name = of_clk_get_parent_name(np, 0); + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, + bypass); + if (IS_ERR(hw)) + return; + + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} + +static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return osc->frequency; +} + +static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_acc) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return osc->accuracy; +} + +static int clk_slow_rc_osc_prepare(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); + + usleep_range(osc->startup_usec, osc->startup_usec + 1); + + return 0; +} + +static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); +} + +static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); +} + +static const struct clk_ops slow_rc_osc_ops = { + .prepare = clk_slow_rc_osc_prepare, + .unprepare = clk_slow_rc_osc_unprepare, + .is_prepared = clk_slow_rc_osc_is_prepared, + .recalc_rate = clk_slow_rc_osc_recalc_rate, + .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, +}; + +static struct clk_hw * __init +at91_clk_register_slow_rc_osc(void __iomem *sckcr, + const char *name, + unsigned long frequency, + unsigned long accuracy, + unsigned long startup) +{ + struct clk_slow_rc_osc *osc; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (!sckcr || !name) + return ERR_PTR(-EINVAL); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &slow_rc_osc_ops; + init.parent_names = NULL; + init.num_parents = 0; + init.flags = CLK_IGNORE_UNUSED; + + osc->hw.init = &init; + osc->sckcr = sckcr; + osc->frequency = frequency; + osc->accuracy = accuracy; + osc->startup_usec = startup; + + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); + if (ret) { + kfree(osc); + hw = ERR_PTR(ret); + } + + return hw; +} + +static void __init +of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk_hw *hw; + u32 frequency = 0; + u32 accuracy = 0; + u32 startup = 0; + const char *name = np->name; + + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "clock-frequency", &frequency); + of_property_read_u32(np, "clock-accuracy", &accuracy); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + + hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, + startup); + if (IS_ERR(hw)) + return; + + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} + +static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); + void __iomem *sckcr = slowck->sckcr; + u32 tmp; + + if (index > 1) + return -EINVAL; + + tmp = readl(sckcr); + + if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || + (index && (tmp & AT91_SCKC_OSCSEL))) + return 0; + + if (index) + tmp |= AT91_SCKC_OSCSEL; + else + tmp &= ~AT91_SCKC_OSCSEL; + + writel(tmp, sckcr); + + usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); + + return 0; +} + +static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); + + return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); +} + +static const struct clk_ops sam9x5_slow_ops = { + .set_parent = clk_sam9x5_slow_set_parent, + .get_parent = clk_sam9x5_slow_get_parent, +}; + +static struct clk_hw * __init +at91_clk_register_sam9x5_slow(void __iomem *sckcr, + const char *name, + const char **parent_names, + int num_parents) +{ + struct clk_sam9x5_slow *slowck; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (!sckcr || !name || !parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); + if (!slowck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9x5_slow_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + slowck->hw.init = &init; + slowck->sckcr = sckcr; + slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); + + hw = &slowck->hw; + ret = clk_hw_register(NULL, &slowck->hw); + if (ret) { + kfree(slowck); + hw = ERR_PTR(ret); + } + + return hw; +} + +static void __init +of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk_hw *hw; + const char *parent_names[2]; + unsigned int num_parents; + const char *name = np->name; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > 2) + return; + + of_clk_parent_fill(np, parent_names, num_parents); + + of_property_read_string(np, "clock-output-names", &name); + + hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, + num_parents); + if (IS_ERR(hw)) + return; + + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} static const struct of_device_id sckc_clk_ids[] __initconst = { /* Slow clock */ @@ -55,3 +426,94 @@ static void __init of_at91sam9x5_sckc_setup(struct device_node *np) } CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", of_at91sam9x5_sckc_setup); + +static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) +{ + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); + + if (osc->prepared) + return 0; + + /* + * Assume that if it has already been selected (for example by the + * bootloader), enough time has aready passed. + */ + if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) { + osc->prepared = true; + return 0; + } + + usleep_range(osc->startup_usec, osc->startup_usec + 1); + osc->prepared = true; + + return 0; +} + +static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) +{ + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); + + return osc->prepared; +} + +static const struct clk_ops sama5d4_slow_osc_ops = { + .prepare = clk_sama5d4_slow_osc_prepare, + .is_prepared = clk_sama5d4_slow_osc_is_prepared, +}; + +static void __init of_sama5d4_sckc_setup(struct device_node *np) +{ + void __iomem *regbase = of_iomap(np, 0); + struct clk_hw *hw; + struct clk_sama5d4_slow_osc *osc; + struct clk_init_data init; + const char *xtal_name; + const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; + bool bypass; + int ret; + + if (!regbase) + return; + + hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0], + NULL, 0, 32768, + 250000000); + if (IS_ERR(hw)) + return; + + xtal_name = of_clk_get_parent_name(np, 0); + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return; + + init.name = parent_names[1]; + init.ops = &sama5d4_slow_osc_ops; + init.parent_names = &xtal_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + + osc->hw.init = &init; + osc->sckcr = regbase; + osc->startup_usec = 1200000; + + if (bypass) + writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase); + + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); + if (ret) { + kfree(osc); + return; + } + + hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2); + if (IS_ERR(hw)) + return; + + of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} +CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", + of_sama5d4_sckc_setup); diff --git a/drivers/clk/at91/sckc.h b/drivers/clk/at91/sckc.h deleted file mode 100644 index 836fcf5..0000000 --- a/drivers/clk/at91/sckc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * drivers/clk/at91/sckc.h - * - * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> - * - * 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. - */ - -#ifndef __AT91_SCKC_H_ -#define __AT91_SCKC_H_ - -extern void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, - void __iomem *sckcr); -extern void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, - void __iomem *sckcr); -extern void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, - void __iomem *sckcr); - -#endif /* __AT91_SCKC_H_ */ diff --git a/drivers/clk/axis/clk-artpec6.c b/drivers/clk/axis/clk-artpec6.c index ffc988b..da1a073 100644 --- a/drivers/clk/axis/clk-artpec6.c +++ b/drivers/clk/axis/clk-artpec6.c @@ -113,8 +113,8 @@ static void of_artpec6_clkctrl_setup(struct device_node *np) of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data); } -CLK_OF_DECLARE(artpec6_clkctrl, "axis,artpec6-clkctrl", - of_artpec6_clkctrl_setup); +CLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl", + of_artpec6_clkctrl_setup); static int artpec6_clkctrl_probe(struct platform_device *pdev) { diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index f287845..f21e9b7 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -19,8 +19,36 @@ config CLK_BCM_KONA in the BCM281xx and BCM21664 families. config COMMON_CLK_IPROC - bool + bool "Broadcom iProc clock support" + depends on ARCH_BCM_IPROC || COMPILE_TEST depends on COMMON_CLK + default ARCH_BCM_IPROC help Enable common clock framework support for Broadcom SoCs based on the iProc architecture + +if COMMON_CLK_IPROC + +config CLK_BCM_CYGNUS + bool "Broadcom Cygnus clock support" + depends on ARCH_BCM_CYGNUS || COMPILE_TEST + default ARCH_BCM_CYGNUS + help + Enable common clock framework support for the Broadcom Cygnus SoC + +config CLK_BCM_NSP + bool "Broadcom Northstar/Northstar Plus clock support" + depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST + default ARCH_BCM_5301X || ARCH_BCM_NSP + help + Enable common clock framework support for the Broadcom Northstar and + Northstar Plus SoCs + +config CLK_BCM_NS2 + bool "Broadcom Northstar 2 clock support" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + Enable common clock framework support for the Broadcom Northstar 2 SoC + +endif diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 1d79bd2..d9dc848 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o -obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o -obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o -obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o -obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o +obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o +obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o +obj-$(CONFIG_CLK_BCM_NSP) += clk-nsp.o +obj-$(CONFIG_CLK_BCM_NS2) += clk-ns2.o diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c index 3a177ad..bd750cf 100644 --- a/drivers/clk/bcm/clk-bcm2835-aux.c +++ b/drivers/clk/bcm/clk-bcm2835-aux.c @@ -25,7 +25,7 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct clk_onecell_data *onecell; + struct clk_hw_onecell_data *onecell; const char *parent; struct clk *parent_clk; struct resource *res; @@ -41,28 +41,24 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev) if (IS_ERR(reg)) return PTR_ERR(reg); - onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL); + onecell = devm_kmalloc(dev, sizeof(*onecell) + sizeof(*onecell->hws) * + BCM2835_AUX_CLOCK_COUNT, GFP_KERNEL); if (!onecell) return -ENOMEM; - onecell->clk_num = BCM2835_AUX_CLOCK_COUNT; - onecell->clks = devm_kcalloc(dev, BCM2835_AUX_CLOCK_COUNT, - sizeof(*onecell->clks), GFP_KERNEL); - if (!onecell->clks) - return -ENOMEM; + onecell->num = BCM2835_AUX_CLOCK_COUNT; gate = reg + BCM2835_AUXENB; - onecell->clks[BCM2835_AUX_CLOCK_UART] = - clk_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL); - - onecell->clks[BCM2835_AUX_CLOCK_SPI1] = - clk_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL); + onecell->hws[BCM2835_AUX_CLOCK_UART] = + clk_hw_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL); - onecell->clks[BCM2835_AUX_CLOCK_SPI2] = - clk_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL); + onecell->hws[BCM2835_AUX_CLOCK_SPI1] = + clk_hw_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL); - of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, onecell); + onecell->hws[BCM2835_AUX_CLOCK_SPI2] = + clk_hw_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL); - return 0; + return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, + onecell); } static const struct of_device_id bcm2835_aux_clk_of_match[] = { diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 7a79708..b68bf57 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -36,6 +36,7 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/clk.h> #include <linux/clk/bcm2835.h> #include <linux/debugfs.h> #include <linux/module.h> @@ -302,8 +303,8 @@ struct bcm2835_cprman { spinlock_t regs_lock; /* spinlock for all clocks */ const char *osc_name; - struct clk_onecell_data onecell; - struct clk *clks[]; + /* Must be last */ + struct clk_hw_onecell_data onecell; }; static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val) @@ -344,24 +345,24 @@ static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base, */ void __init bcm2835_init_clocks(void) { - struct clk *clk; + struct clk_hw *hw; int ret; - clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000); - if (IS_ERR(clk)) + hw = clk_hw_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000); + if (IS_ERR(hw)) pr_err("apb_pclk not registered\n"); - clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000); - if (IS_ERR(clk)) + hw = clk_hw_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000); + if (IS_ERR(hw)) pr_err("uart0_pclk not registered\n"); - ret = clk_register_clkdev(clk, NULL, "20201000.uart"); + ret = clk_hw_register_clkdev(hw, NULL, "20201000.uart"); if (ret) pr_err("uart0_pclk alias not registered\n"); - clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000); - if (IS_ERR(clk)) + hw = clk_hw_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000); + if (IS_ERR(hw)) pr_err("uart1_pclk not registered\n"); - ret = clk_register_clkdev(clk, NULL, "20215000.uart"); + ret = clk_hw_register_clkdev(hw, NULL, "20215000.uart"); if (ret) pr_err("uart1_pclk alias not registered\n"); } @@ -443,6 +444,8 @@ struct bcm2835_clock_data { /* Number of fractional bits in the divider */ u32 frac_bits; + u32 flags; + bool is_vpu_clock; bool is_mash_clock; }; @@ -1006,16 +1009,28 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw, return 0; } +static bool +bcm2835_clk_is_pllc(struct clk_hw *hw) +{ + if (!hw) + return false; + + return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0; +} + static int bcm2835_clock_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct clk_hw *parent, *best_parent = NULL; + bool current_parent_is_pllc; unsigned long rate, best_rate = 0; unsigned long prate, best_prate = 0; size_t i; u32 div; + current_parent_is_pllc = bcm2835_clk_is_pllc(clk_hw_get_parent(hw)); + /* * Select parent clock that results in the closest but lower rate */ @@ -1023,6 +1038,17 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw, parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; + + /* + * Don't choose a PLLC-derived clock as our parent + * unless it had been manually set that way. PLLC's + * frequency gets adjusted by the firmware due to + * over-temp or under-voltage conditions, without + * prior notification to our clock consumer. + */ + if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc) + continue; + prate = clk_hw_get_rate(parent); div = bcm2835_clock_choose_div(hw, req->rate, prate, true); rate = bcm2835_clock_rate_from_divisor(clock, prate, div); @@ -1121,11 +1147,12 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = { .debug_init = bcm2835_clock_debug_init, }; -static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman, - const struct bcm2835_pll_data *data) +static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, + const struct bcm2835_pll_data *data) { struct bcm2835_pll *pll; struct clk_init_data init; + int ret; memset(&init, 0, sizeof(init)); @@ -1144,17 +1171,20 @@ static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman, pll->data = data; pll->hw.init = &init; - return devm_clk_register(cprman->dev, &pll->hw); + ret = devm_clk_hw_register(cprman->dev, &pll->hw); + if (ret) + return NULL; + return &pll->hw; } -static struct clk * +static struct clk_hw * bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, const struct bcm2835_pll_divider_data *data) { struct bcm2835_pll_divider *divider; struct clk_init_data init; - struct clk *clk; const char *divider_name; + int ret; if (data->fixed_divider != 1) { divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL, @@ -1188,32 +1218,33 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, divider->cprman = cprman; divider->data = data; - clk = devm_clk_register(cprman->dev, ÷r->div.hw); - if (IS_ERR(clk)) - return clk; + ret = devm_clk_hw_register(cprman->dev, ÷r->div.hw); + if (ret) + return ERR_PTR(ret); /* * PLLH's channels have a fixed divide by 10 afterwards, which * is what our consumers are actually using. */ if (data->fixed_divider != 1) { - return clk_register_fixed_factor(cprman->dev, data->name, - divider_name, - CLK_SET_RATE_PARENT, - 1, - data->fixed_divider); + return clk_hw_register_fixed_factor(cprman->dev, data->name, + divider_name, + CLK_SET_RATE_PARENT, + 1, + data->fixed_divider); } - return clk; + return ÷r->div.hw; } -static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, +static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman, const struct bcm2835_clock_data *data) { struct bcm2835_clock *clock; struct clk_init_data init; const char *parents[1 << CM_SRC_BITS]; size_t i; + int ret; /* * Replace our "xosc" references with the oscillator's @@ -1230,13 +1261,19 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, init.parent_names = parents; init.num_parents = data->num_mux_parents; init.name = data->name; - init.flags = CLK_IGNORE_UNUSED; + init.flags = data->flags | CLK_IGNORE_UNUSED; if (data->is_vpu_clock) { init.ops = &bcm2835_vpu_clock_clk_ops; } else { init.ops = &bcm2835_clock_clk_ops; init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + /* If the clock wasn't actually enabled at boot, it's not + * critical. + */ + if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE)) + init.flags &= ~CLK_IS_CRITICAL; } clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL); @@ -1247,7 +1284,10 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, clock->data = data; clock->hw.init = &init; - return devm_clk_register(cprman->dev, &clock->hw); + ret = devm_clk_hw_register(cprman->dev, &clock->hw); + if (ret) + return ERR_PTR(ret); + return &clock->hw; } static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman, @@ -1259,8 +1299,8 @@ static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman, CM_GATE_BIT, 0, &cprman->regs_lock); } -typedef struct clk *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman, - const void *data); +typedef struct clk_hw *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman, + const void *data); struct bcm2835_clk_desc { bcm2835_clk_register clk_register; const void *data; @@ -1649,6 +1689,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .div_reg = CM_VPUDIV, .int_bits = 12, .frac_bits = 8, + .flags = CLK_IS_CRITICAL, .is_vpu_clock = true), /* clocks with per parent mux */ @@ -1705,13 +1746,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .div_reg = CM_GP1DIV, .int_bits = 12, .frac_bits = 12, + .flags = CLK_IS_CRITICAL, .is_mash_clock = true), [BCM2835_CLOCK_GP2] = REGISTER_PER_CLK( .name = "gp2", .ctl_reg = CM_GP2CTL, .div_reg = CM_GP2DIV, .int_bits = 12, - .frac_bits = 12), + .frac_bits = 12, + .flags = CLK_IS_CRITICAL), /* HDMI state machine */ [BCM2835_CLOCK_HSM] = REGISTER_PER_CLK( @@ -1790,18 +1833,38 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_PERIICTL), }; +/* + * Permanently take a reference on the parent of the SDRAM clock. + * + * While the SDRAM is being driven by its dedicated PLL most of the + * time, there is a little loop running in the firmware that + * periodically switches the SDRAM to using our CM clock to do PVT + * recalibration, with the assumption that the previously configured + * SDRAM parent is still enabled and running. + */ +static int bcm2835_mark_sdc_parent_critical(struct clk *sdc) +{ + struct clk *parent = clk_get_parent(sdc); + + if (IS_ERR(parent)) + return PTR_ERR(parent); + + return clk_prepare_enable(parent); +} + static int bcm2835_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct clk **clks; + struct clk_hw **hws; struct bcm2835_cprman *cprman; struct resource *res; const struct bcm2835_clk_desc *desc; const size_t asize = ARRAY_SIZE(clk_desc_array); size_t i; + int ret; - cprman = devm_kzalloc(dev, - sizeof(*cprman) + asize * sizeof(*clks), + cprman = devm_kzalloc(dev, sizeof(*cprman) + + sizeof(*cprman->onecell.hws) * asize, GFP_KERNEL); if (!cprman) return -ENOMEM; @@ -1819,18 +1882,21 @@ static int bcm2835_clk_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cprman); - cprman->onecell.clk_num = asize; - cprman->onecell.clks = cprman->clks; - clks = cprman->clks; + cprman->onecell.num = asize; + hws = cprman->onecell.hws; for (i = 0; i < asize; i++) { desc = &clk_desc_array[i]; if (desc->clk_register && desc->data) - clks[i] = desc->clk_register(cprman, desc->data); + hws[i] = desc->clk_register(cprman, desc->data); } - return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, - &cprman->onecell); + ret = bcm2835_mark_sdc_parent_critical(hws[BCM2835_CLOCK_SDRAM]->clk); + if (ret) + return ret; + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + &cprman->onecell); } static const struct of_device_id bcm2835_clk_of_match[] = { diff --git a/drivers/clk/bcm/clk-bcm53573-ilp.c b/drivers/clk/bcm/clk-bcm53573-ilp.c new file mode 100644 index 0000000..36eb371 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm53573-ilp.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki <rafal@milecki.pl> + * + * 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/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define PMU_XTAL_FREQ_RATIO 0x66c +#define XTAL_ALP_PER_4ILP 0x00001fff +#define XTAL_CTL_EN 0x80000000 +#define PMU_SLOW_CLK_PERIOD 0x6dc + +struct bcm53573_ilp { + struct clk_hw hw; + struct regmap *regmap; +}; + +static int bcm53573_ilp_enable(struct clk_hw *hw) +{ + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw); + + regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0x10199); + regmap_write(ilp->regmap, 0x674, 0x10000); + + return 0; +} + +static void bcm53573_ilp_disable(struct clk_hw *hw) +{ + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw); + + regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0); + regmap_write(ilp->regmap, 0x674, 0); +} + +static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw); + struct regmap *regmap = ilp->regmap; + u32 last_val, cur_val; + int sum = 0, num = 0, loop_num = 0; + int avg; + + /* Enable measurement */ + regmap_write(regmap, PMU_XTAL_FREQ_RATIO, XTAL_CTL_EN); + + /* Read initial value */ + regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &last_val); + last_val &= XTAL_ALP_PER_4ILP; + + /* + * At minimum we should loop for a bit to let hardware do the + * measurement. This isn't very accurate however, so for a better + * precision lets try getting 20 different values for and use average. + */ + while (num < 20) { + regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val); + cur_val &= XTAL_ALP_PER_4ILP; + + if (cur_val != last_val) { + /* Got different value, use it */ + sum += cur_val; + num++; + loop_num = 0; + last_val = cur_val; + } else if (++loop_num > 5000) { + /* Same value over and over, give up */ + sum += cur_val; + num++; + break; + } + + cpu_relax(); + } + + /* Disable measurement to save power */ + regmap_write(regmap, PMU_XTAL_FREQ_RATIO, 0x0); + + avg = sum / num; + + return parent_rate * 4 / avg; +} + +static const struct clk_ops bcm53573_ilp_clk_ops = { + .enable = bcm53573_ilp_enable, + .disable = bcm53573_ilp_disable, + .recalc_rate = bcm53573_ilp_recalc_rate, +}; + +static void bcm53573_ilp_init(struct device_node *np) +{ + struct bcm53573_ilp *ilp; + struct clk_init_data init = { }; + const char *parent_name; + int err; + + ilp = kzalloc(sizeof(*ilp), GFP_KERNEL); + if (!ilp) + return; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) { + err = -ENOENT; + goto err_free_ilp; + } + + ilp->regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(ilp->regmap)) { + err = PTR_ERR(ilp->regmap); + goto err_free_ilp; + } + + init.name = np->name; + init.ops = &bcm53573_ilp_clk_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + ilp->hw.init = &init; + err = clk_hw_register(NULL, &ilp->hw); + if (err) + goto err_free_ilp; + + err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw); + if (err) + goto err_clk_hw_unregister; + + return; + +err_clk_hw_unregister: + clk_hw_unregister(&ilp->hw); +err_free_ilp: + kfree(ilp); + pr_err("Failed to init ILP clock: %d\n", err); +} + +/* We need it very early for arch code, before device model gets ready */ +CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init); diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 526b0b0..c37a7f0 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -586,8 +586,8 @@ static u32 *parent_process(const char *clocks[], } /* There is at least one parent, so allocate a selector array */ - - parent_sel = kmalloc(parent_count * sizeof(*parent_sel), GFP_KERNEL); + parent_sel = kmalloc_array(parent_count, sizeof(*parent_sel), + GFP_KERNEL); if (!parent_sel) { pr_err("%s: error allocating %u parent selectors\n", __func__, parent_count); @@ -696,77 +696,69 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk) bcm_clk->type = bcm_clk_none; } -static void kona_clk_teardown(struct clk *clk) +static void kona_clk_teardown(struct clk_hw *hw) { - struct clk_hw *hw; struct kona_clk *bcm_clk; - if (!clk) + if (!hw) return; - hw = __clk_get_hw(clk); - if (!hw) { - pr_err("%s: clk %p has null hw pointer\n", __func__, clk); - return; - } - clk_unregister(clk); + clk_hw_unregister(hw); bcm_clk = to_kona_clk(hw); bcm_clk_teardown(bcm_clk); } -struct clk *kona_clk_setup(struct kona_clk *bcm_clk) +static int kona_clk_setup(struct kona_clk *bcm_clk) { + int ret; struct clk_init_data *init_data = &bcm_clk->init_data; - struct clk *clk = NULL; switch (bcm_clk->type) { case bcm_clk_peri: - if (peri_clk_setup(bcm_clk->u.data, init_data)) - return NULL; + ret = peri_clk_setup(bcm_clk->u.data, init_data); + if (ret) + return ret; break; default: pr_err("%s: clock type %d invalid for %s\n", __func__, (int)bcm_clk->type, init_data->name); - return NULL; + return -EINVAL; } /* Make sure everything makes sense before we set it up */ if (!kona_clk_valid(bcm_clk)) { pr_err("%s: clock data invalid for %s\n", __func__, init_data->name); + ret = -EINVAL; goto out_teardown; } bcm_clk->hw.init = init_data; - clk = clk_register(NULL, &bcm_clk->hw); - if (IS_ERR(clk)) { - pr_err("%s: error registering clock %s (%ld)\n", __func__, - init_data->name, PTR_ERR(clk)); + ret = clk_hw_register(NULL, &bcm_clk->hw); + if (ret) { + pr_err("%s: error registering clock %s (%d)\n", __func__, + init_data->name, ret); goto out_teardown; } - BUG_ON(!clk); - return clk; + return 0; out_teardown: bcm_clk_teardown(bcm_clk); - return NULL; + return ret; } static void ccu_clks_teardown(struct ccu_data *ccu) { u32 i; - for (i = 0; i < ccu->clk_data.clk_num; i++) - kona_clk_teardown(ccu->clk_data.clks[i]); - kfree(ccu->clk_data.clks); + for (i = 0; i < ccu->clk_num; i++) + kona_clk_teardown(&ccu->kona_clks[i].hw); } static void kona_ccu_teardown(struct ccu_data *ccu) { - kfree(ccu->clk_data.clks); - ccu->clk_data.clks = NULL; if (!ccu->base) return; @@ -793,6 +785,20 @@ static bool ccu_data_valid(struct ccu_data *ccu) return true; } +static struct clk_hw * +of_clk_kona_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct ccu_data *ccu = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= ccu->clk_num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &ccu->kona_clks[idx].hw; +} + /* * Set up a CCU. Call the provided ccu_clks_setup callback to * initialize the array of clocks provided by the CCU. @@ -805,18 +811,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, unsigned int i; int ret; - if (ccu->clk_data.clk_num) { - size_t size; - - size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks); - ccu->clk_data.clks = kzalloc(size, GFP_KERNEL); - if (!ccu->clk_data.clks) { - pr_err("%s: unable to allocate %u clocks for %s\n", - __func__, ccu->clk_data.clk_num, node->name); - return; - } - } - ret = of_address_to_resource(node, 0, &res); if (ret) { pr_err("%s: no valid CCU registers found for %s\n", __func__, @@ -851,13 +845,13 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, * the clock framework clock array (in ccu->data). Then * register as a provider for these clocks. */ - for (i = 0; i < ccu->clk_data.clk_num; i++) { + for (i = 0; i < ccu->clk_num; i++) { if (!ccu->kona_clks[i].ccu) continue; - ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]); + kona_clk_setup(&ccu->kona_clks[i]); } - ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data); + ret = of_clk_add_hw_provider(node, of_clk_kona_onecell_get, ccu); if (ret) { pr_err("%s: error adding ccu %s as provider (%d)\n", __func__, node->name, ret); diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 3a15347..eee64b9 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1256,19 +1256,18 @@ bool __init kona_ccu_init(struct ccu_data *ccu) { unsigned long flags; unsigned int which; - struct clk **clks = ccu->clk_data.clks; struct kona_clk *kona_clks = ccu->kona_clks; bool success = true; flags = ccu_lock(ccu); __ccu_write_enable(ccu); - for (which = 0; which < ccu->clk_data.clk_num; which++) { - struct kona_clk *bcm_clk; + for (which = 0; which < ccu->clk_num; which++) { + struct kona_clk *bcm_clk = &kona_clks[which]; - if (!clks[which]) + if (!bcm_clk->ccu) continue; - bcm_clk = &kona_clks[which]; + success &= __kona_clk_init(bcm_clk); } diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 906576e..f4b39bb 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -481,7 +481,7 @@ struct ccu_data { bool write_enabled; /* write access is currently enabled */ struct ccu_policy policy; struct device_node *node; - struct clk_onecell_data clk_data; + size_t clk_num; const char *name; u32 range; /* byte range of address space */ struct kona_clk kona_clks[]; /* must be last */ @@ -491,9 +491,7 @@ struct ccu_data { #define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ .name = #_name "_ccu", \ .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ - .clk_data = { \ - .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ - } + .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT /* Exported globals */ @@ -505,7 +503,6 @@ extern u64 scaled_div_max(struct bcm_clk_div *div); extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths); -extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk); extern void __init kona_dt_ccu_setup(struct ccu_data *ccu, struct device_node *node); extern bool __init kona_ccu_init(struct ccu_data *ccu); diff --git a/drivers/clk/berlin/berlin2-avpll.c b/drivers/clk/berlin/berlin2-avpll.c index fd0f26c..cfcae46 100644 --- a/drivers/clk/berlin/berlin2-avpll.c +++ b/drivers/clk/berlin/berlin2-avpll.c @@ -188,7 +188,7 @@ static const struct clk_ops berlin2_avpll_vco_ops = { .recalc_rate = berlin2_avpll_vco_recalc_rate, }; -struct clk * __init berlin2_avpll_vco_register(void __iomem *base, +int __init berlin2_avpll_vco_register(void __iomem *base, const char *name, const char *parent_name, u8 vco_flags, unsigned long flags) { @@ -197,7 +197,7 @@ struct clk * __init berlin2_avpll_vco_register(void __iomem *base, vco = kzalloc(sizeof(*vco), GFP_KERNEL); if (!vco) - return ERR_PTR(-ENOMEM); + return -ENOMEM; vco->base = base; vco->flags = vco_flags; @@ -208,7 +208,7 @@ struct clk * __init berlin2_avpll_vco_register(void __iomem *base, init.num_parents = 1; init.flags = flags; - return clk_register(NULL, &vco->hw); + return clk_hw_register(NULL, &vco->hw); } struct berlin2_avpll_channel { @@ -364,7 +364,7 @@ static const struct clk_ops berlin2_avpll_channel_ops = { */ static const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 }; -struct clk * __init berlin2_avpll_channel_register(void __iomem *base, +int __init berlin2_avpll_channel_register(void __iomem *base, const char *name, u8 index, const char *parent_name, u8 ch_flags, unsigned long flags) { @@ -373,7 +373,7 @@ struct clk * __init berlin2_avpll_channel_register(void __iomem *base, ch = kzalloc(sizeof(*ch), GFP_KERNEL); if (!ch) - return ERR_PTR(-ENOMEM); + return -ENOMEM; ch->base = base; if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK) @@ -389,5 +389,5 @@ struct clk * __init berlin2_avpll_channel_register(void __iomem *base, init.num_parents = 1; init.flags = flags; - return clk_register(NULL, &ch->hw); + return clk_hw_register(NULL, &ch->hw); } diff --git a/drivers/clk/berlin/berlin2-avpll.h b/drivers/clk/berlin/berlin2-avpll.h index a37f506..17e3111 100644 --- a/drivers/clk/berlin/berlin2-avpll.h +++ b/drivers/clk/berlin/berlin2-avpll.h @@ -19,17 +19,13 @@ #ifndef __BERLIN2_AVPLL_H #define __BERLIN2_AVPLL_H -struct clk; - #define BERLIN2_AVPLL_BIT_QUIRK BIT(0) #define BERLIN2_AVPLL_SCRAMBLE_QUIRK BIT(1) -struct clk * __init -berlin2_avpll_vco_register(void __iomem *base, const char *name, +int berlin2_avpll_vco_register(void __iomem *base, const char *name, const char *parent_name, u8 vco_flags, unsigned long flags); -struct clk * __init -berlin2_avpll_channel_register(void __iomem *base, const char *name, +int berlin2_avpll_channel_register(void __iomem *base, const char *name, u8 index, const char *parent_name, u8 ch_flags, unsigned long flags); diff --git a/drivers/clk/berlin/berlin2-div.c b/drivers/clk/berlin/berlin2-div.c index 81ff97f..41ab2d3 100644 --- a/drivers/clk/berlin/berlin2-div.c +++ b/drivers/clk/berlin/berlin2-div.c @@ -234,7 +234,7 @@ static const struct clk_ops berlin2_div_mux_ops = { .get_parent = berlin2_div_get_parent, }; -struct clk * __init +struct clk_hw * __init berlin2_div_register(const struct berlin2_div_map *map, void __iomem *base, const char *name, u8 div_flags, const char **parent_names, int num_parents, @@ -259,7 +259,7 @@ berlin2_div_register(const struct berlin2_div_map *map, if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0) mux_ops = NULL; - return clk_register_composite(NULL, name, parent_names, num_parents, + return clk_hw_register_composite(NULL, name, parent_names, num_parents, &div->hw, mux_ops, &div->hw, rate_ops, &div->hw, gate_ops, flags); } diff --git a/drivers/clk/berlin/berlin2-div.h b/drivers/clk/berlin/berlin2-div.h index 15e3384..e835ddf 100644 --- a/drivers/clk/berlin/berlin2-div.h +++ b/drivers/clk/berlin/berlin2-div.h @@ -19,7 +19,7 @@ #ifndef __BERLIN2_DIV_H #define __BERLIN2_DIV_H -struct clk; +struct clk_hw; #define BERLIN2_DIV_HAS_GATE BIT(0) #define BERLIN2_DIV_HAS_MUX BIT(1) @@ -80,7 +80,7 @@ struct berlin2_div_data { u8 div_flags; }; -struct clk * __init +struct clk_hw * berlin2_div_register(const struct berlin2_div_map *map, void __iomem *base, const char *name, u8 div_flags, const char **parent_names, int num_parents, diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c index 1c2294d..4ffbe80 100644 --- a/drivers/clk/berlin/berlin2-pll.c +++ b/drivers/clk/berlin/berlin2-pll.c @@ -84,7 +84,7 @@ static const struct clk_ops berlin2_pll_ops = { .recalc_rate = berlin2_pll_recalc_rate, }; -struct clk * __init +int __init berlin2_pll_register(const struct berlin2_pll_map *map, void __iomem *base, const char *name, const char *parent_name, unsigned long flags) @@ -94,7 +94,7 @@ berlin2_pll_register(const struct berlin2_pll_map *map, pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) - return ERR_PTR(-ENOMEM); + return -ENOMEM; /* copy pll_map to allow __initconst */ memcpy(&pll->map, map, sizeof(*map)); @@ -106,5 +106,5 @@ berlin2_pll_register(const struct berlin2_pll_map *map, init.num_parents = 1; init.flags = flags; - return clk_register(NULL, &pll->hw); + return clk_hw_register(NULL, &pll->hw); } diff --git a/drivers/clk/berlin/berlin2-pll.h b/drivers/clk/berlin/berlin2-pll.h index 8831ce2..583e024 100644 --- a/drivers/clk/berlin/berlin2-pll.h +++ b/drivers/clk/berlin/berlin2-pll.h @@ -19,8 +19,6 @@ #ifndef __BERLIN2_PLL_H #define __BERLIN2_PLL_H -struct clk; - struct berlin2_pll_map { const u8 vcodiv[16]; u8 mult; @@ -29,9 +27,8 @@ struct berlin2_pll_map { u8 divsel_shift; }; -struct clk * __init -berlin2_pll_register(const struct berlin2_pll_map *map, - void __iomem *base, const char *name, - const char *parent_name, unsigned long flags); +int berlin2_pll_register(const struct berlin2_pll_map *map, + void __iomem *base, const char *name, + const char *parent_name, unsigned long flags); #endif /* __BERLIN2_PLL_H */ diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c index 23e0e3b..edf3b96 100644 --- a/drivers/clk/berlin/bg2.c +++ b/drivers/clk/berlin/bg2.c @@ -92,8 +92,7 @@ */ #define MAX_CLKS 41 -static struct clk *clks[MAX_CLKS]; -static struct clk_onecell_data clk_data; +static struct clk_hw_onecell_data *clk_data; static DEFINE_SPINLOCK(lock); static void __iomem *gbase; @@ -505,8 +504,17 @@ static void __init berlin2_clock_setup(struct device_node *np) struct device_node *parent_np = of_get_parent(np); const char *parent_names[9]; struct clk *clk; + struct clk_hw *hw; + struct clk_hw **hws; u8 avpll_flags = 0; - int n; + int n, ret; + + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL); + if (!clk_data) + return; + clk_data->num = MAX_CLKS; + hws = clk_data->hws; gbase = of_iomap(parent_np, 0); if (!gbase) @@ -526,118 +534,118 @@ static void __init berlin2_clock_setup(struct device_node *np) } /* simple register PLLs */ - clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0, + ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0, clk_names[SYSPLL], clk_names[REFCLK], 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; - clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0, + ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0, clk_names[MEMPLL], clk_names[REFCLK], 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; - clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0, + ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0, clk_names[CPUPLL], clk_names[REFCLK], 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; if (of_device_is_compatible(np, "marvell,berlin2-global-register")) avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK; /* audio/video VCOs */ - clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA", + ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA", clk_names[REFCLK], avpll_flags, 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; for (n = 0; n < 8; n++) { - clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0, + ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0, clk_names[AVPLL_A1 + n], n, "avpll_vcoA", avpll_flags, 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; } - clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB", + ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB", clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; for (n = 0; n < 8; n++) { - clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31, + ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31, clk_names[AVPLL_B1 + n], n, "avpll_vcoB", BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0); - if (IS_ERR(clk)) + if (ret) goto bg2_fail; } /* reference clock bypass switches */ parent_names[0] = clk_names[SYSPLL]; parent_names[1] = clk_names[REFCLK]; - clk = clk_register_mux(NULL, "syspll_byp", parent_names, 2, + hw = clk_hw_register_mux(NULL, "syspll_byp", parent_names, 2, 0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; - clk_names[SYSPLL] = __clk_get_name(clk); + clk_names[SYSPLL] = clk_hw_get_name(hw); parent_names[0] = clk_names[MEMPLL]; parent_names[1] = clk_names[REFCLK]; - clk = clk_register_mux(NULL, "mempll_byp", parent_names, 2, + hw = clk_hw_register_mux(NULL, "mempll_byp", parent_names, 2, 0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; - clk_names[MEMPLL] = __clk_get_name(clk); + clk_names[MEMPLL] = clk_hw_get_name(hw); parent_names[0] = clk_names[CPUPLL]; parent_names[1] = clk_names[REFCLK]; - clk = clk_register_mux(NULL, "cpupll_byp", parent_names, 2, + hw = clk_hw_register_mux(NULL, "cpupll_byp", parent_names, 2, 0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; - clk_names[CPUPLL] = __clk_get_name(clk); + clk_names[CPUPLL] = clk_hw_get_name(hw); /* clock muxes */ parent_names[0] = clk_names[AVPLL_B3]; parent_names[1] = clk_names[AVPLL_A3]; - clk = clk_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2, 0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; parent_names[0] = clk_names[VIDEO0_PLL]; parent_names[1] = clk_names[VIDEO_EXT0]; - clk = clk_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2, 0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; parent_names[0] = clk_names[VIDEO1_PLL]; parent_names[1] = clk_names[VIDEO_EXT0]; - clk = clk_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2, 0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; parent_names[0] = clk_names[AVPLL_A2]; parent_names[1] = clk_names[AVPLL_B2]; - clk = clk_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2, 0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; parent_names[0] = clk_names[VIDEO2_PLL]; parent_names[1] = clk_names[VIDEO_EXT0]; - clk = clk_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2, 0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; parent_names[0] = clk_names[AVPLL_B1]; parent_names[1] = clk_names[AVPLL_A5]; - clk = clk_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2, + hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2, 0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock); - if (IS_ERR(clk)) + if (IS_ERR(hw)) goto bg2_fail; /* clock divider cells */ @@ -648,7 +656,7 @@ static void __init berlin2_clock_setup(struct device_node *np) for (k = 0; k < dd->num_parents; k++) parent_names[k] = clk_names[dd->parent_ids[k]]; - clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, + hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, dd->name, dd->div_flags, parent_names, dd->num_parents, dd->flags, &lock); } @@ -657,18 +665,18 @@ static void __init berlin2_clock_setup(struct device_node *np) for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) { const struct berlin2_gate_data *gd = &bg2_gates[n]; - clks[CLKID_GETH0 + n] = clk_register_gate(NULL, gd->name, + hws[CLKID_GETH0 + n] = clk_hw_register_gate(NULL, gd->name, gd->parent_name, gd->flags, gbase + REG_CLKENABLE, gd->bit_idx, 0, &lock); } /* twdclk is derived from cpu/3 */ - clks[CLKID_TWD] = - clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); + hws[CLKID_TWD] = + clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); /* check for errors on leaf clocks */ for (n = 0; n < MAX_CLKS; n++) { - if (!IS_ERR(clks[n])) + if (!IS_ERR(hws[n])) continue; pr_err("%s: Unable to register leaf clock %d\n", @@ -677,9 +685,7 @@ static void __init berlin2_clock_setup(struct device_node *np) } /* register clk-provider */ - clk_data.clks = clks; - clk_data.clk_num = MAX_CLKS; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data); return; diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index f144547..0718e83 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -46,8 +46,7 @@ #define REG_SDIO1XIN_CLKCTL 0x015c #define MAX_CLKS 28 -static struct clk *clks[MAX_CLKS]; -static struct clk_onecell_data clk_data; +static struct clk_hw_onecell_data *clk_data; static DEFINE_SPINLOCK(lock); static void __iomem *gbase; static void __iomem *cpupll_base; @@ -293,7 +292,15 @@ static void __init berlin2q_clock_setup(struct device_node *np) struct device_node *parent_np = of_get_parent(np); const char *parent_names[9]; struct clk *clk; - int n; + struct clk_hw **hws; + int n, ret; + + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL); + if (!clk_data) + return; + clk_data->num = MAX_CLKS; + hws = clk_data->hws; gbase = of_iomap(parent_np, 0); if (!gbase) { @@ -317,14 +324,14 @@ static void __init berlin2q_clock_setup(struct device_node *np) } /* simple register PLLs */ - clk = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0, + ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0, clk_names[SYSPLL], clk_names[REFCLK], 0); - if (IS_ERR(clk)) + if (ret) goto bg2q_fail; - clk = berlin2_pll_register(&bg2q_pll_map, cpupll_base, + ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base, clk_names[CPUPLL], clk_names[REFCLK], 0); - if (IS_ERR(clk)) + if (ret) goto bg2q_fail; /* TODO: add BG2Q AVPLL */ @@ -342,7 +349,7 @@ static void __init berlin2q_clock_setup(struct device_node *np) for (k = 0; k < dd->num_parents; k++) parent_names[k] = clk_names[dd->parent_ids[k]]; - clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, + hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, dd->name, dd->div_flags, parent_names, dd->num_parents, dd->flags, &lock); } @@ -351,22 +358,22 @@ static void __init berlin2q_clock_setup(struct device_node *np) for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) { const struct berlin2_gate_data *gd = &bg2q_gates[n]; - clks[CLKID_GFX2DAXI + n] = clk_register_gate(NULL, gd->name, + hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name, gd->parent_name, gd->flags, gbase + REG_CLKENABLE, gd->bit_idx, 0, &lock); } /* cpuclk divider is fixed to 1 */ - clks[CLKID_CPU] = - clk_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL], + hws[CLKID_CPU] = + clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL], 0, 1, 1); /* twdclk is derived from cpu/3 */ - clks[CLKID_TWD] = - clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); + hws[CLKID_TWD] = + clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); /* check for errors on leaf clocks */ for (n = 0; n < MAX_CLKS; n++) { - if (!IS_ERR(clks[n])) + if (!IS_ERR(hws[n])) continue; pr_err("%s: Unable to register leaf clock %d\n", @@ -375,9 +382,7 @@ static void __init berlin2q_clock_setup(struct device_node *np) } /* register clk-provider */ - clk_data.clks = clks; - clk_data.clk_num = MAX_CLKS; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data); return; diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index 90897af..ea85685 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -68,8 +68,7 @@ #define HW_LCDCLKDIV 0x01fc #define HW_ADCANACLKDIV 0x0200 -static struct clk *clks[MAX_CLKS]; -static struct clk_onecell_data clk_data; +static struct clk_hw_onecell_data *clk_data; static DEFINE_SPINLOCK(asm9260_clk_lock); struct asm9260_div_clk { @@ -267,12 +266,20 @@ static struct asm9260_mux_clock asm9260_mux_clks[] __initdata = { static void __init asm9260_acc_init(struct device_node *np) { - struct clk *clk; + struct clk_hw *hw; + struct clk_hw **hws; const char *ref_clk, *pll_clk = "pll"; u32 rate; int n; u32 accuracy = 0; + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL); + if (!clk_data) + return; + clk_data->num = MAX_CLKS; + hws = clk_data->hws; + base = of_io_request_and_map(np, 0, np->name); if (IS_ERR(base)) panic("%s: unable to map resource", np->name); @@ -282,10 +289,10 @@ static void __init asm9260_acc_init(struct device_node *np) ref_clk = of_clk_get_parent_name(np, 0); accuracy = clk_get_accuracy(__clk_lookup(ref_clk)); - clk = clk_register_fixed_rate_with_accuracy(NULL, pll_clk, + hw = clk_hw_register_fixed_rate_with_accuracy(NULL, pll_clk, ref_clk, 0, rate, accuracy); - if (IS_ERR(clk)) + if (IS_ERR(hw)) panic("%s: can't register REFCLK. Check DT!", np->name); for (n = 0; n < ARRAY_SIZE(asm9260_mux_clks); n++) { @@ -293,7 +300,7 @@ static void __init asm9260_acc_init(struct device_node *np) mc->parent_names[0] = ref_clk; mc->parent_names[1] = pll_clk; - clk = clk_register_mux_table(NULL, mc->name, mc->parent_names, + hw = clk_hw_register_mux_table(NULL, mc->name, mc->parent_names, mc->num_parents, mc->flags, base + mc->offset, 0, mc->mask, 0, mc->table, &asm9260_clk_lock); } @@ -302,7 +309,7 @@ static void __init asm9260_acc_init(struct device_node *np) for (n = 0; n < ARRAY_SIZE(asm9260_mux_gates); n++) { const struct asm9260_gate_data *gd = &asm9260_mux_gates[n]; - clk = clk_register_gate(NULL, gd->name, + hw = clk_hw_register_gate(NULL, gd->name, gd->parent_name, gd->flags | CLK_SET_RATE_PARENT, base + gd->reg, gd->bit_idx, 0, &asm9260_clk_lock); } @@ -311,7 +318,7 @@ static void __init asm9260_acc_init(struct device_node *np) for (n = 0; n < ARRAY_SIZE(asm9260_div_clks); n++) { const struct asm9260_div_clk *dc = &asm9260_div_clks[n]; - clks[dc->idx] = clk_register_divider(NULL, dc->name, + hws[dc->idx] = clk_hw_register_divider(NULL, dc->name, dc->parent_name, CLK_SET_RATE_PARENT, base + dc->reg, 0, 8, CLK_DIVIDER_ONE_BASED, &asm9260_clk_lock); @@ -321,14 +328,14 @@ static void __init asm9260_acc_init(struct device_node *np) for (n = 0; n < ARRAY_SIZE(asm9260_ahb_gates); n++) { const struct asm9260_gate_data *gd = &asm9260_ahb_gates[n]; - clks[gd->idx] = clk_register_gate(NULL, gd->name, + hws[gd->idx] = clk_hw_register_gate(NULL, gd->name, gd->parent_name, gd->flags, base + gd->reg, gd->bit_idx, 0, &asm9260_clk_lock); } /* check for errors on leaf clocks */ for (n = 0; n < MAX_CLKS; n++) { - if (!IS_ERR(clks[n])) + if (!IS_ERR(hws[n])) continue; pr_err("%s: Unable to register leaf clock %d\n", @@ -337,9 +344,7 @@ static void __init asm9260_acc_init(struct device_node *np) } /* register clk-provider */ - clk_data.clks = clks; - clk_data.clk_num = MAX_CLKS; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); return; fail: iounmap(base); diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 3294db3..5e918e7 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -392,8 +392,8 @@ static int axi_clkgen_probe(struct platform_device *pdev) const char *parent_names[2]; const char *clk_name; struct resource *mem; - struct clk *clk; unsigned int i; + int ret; if (!pdev->dev.of_node) return -ENODEV; @@ -433,12 +433,12 @@ static int axi_clkgen_probe(struct platform_device *pdev) axi_clkgen_mmcm_enable(axi_clkgen, false); axi_clkgen->clk_hw.init = &init; - clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(&pdev->dev, &axi_clkgen->clk_hw); + if (ret) + return ret; - return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, - clk); + return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_simple_get, + &axi_clkgen->clk_hw); } static int axi_clkgen_remove(struct platform_device *pdev) diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c index c7c91a5..5d7ae33 100644 --- a/drivers/clk/clk-axm5516.c +++ b/drivers/clk/clk-axm5516.c @@ -516,6 +516,19 @@ static struct axxia_clk *axmclk_clocks[] = { [AXXIA_CLK_MMC] = &clk_mmc_mux.aclk, }; +static struct clk_hw * +of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused) +{ + unsigned int idx = clkspec->args[0]; + + if (idx >= ARRAY_SIZE(axmclk_clocks)) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &axmclk_clocks[idx]->hw; +} + static const struct regmap_config axmclk_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -530,21 +543,14 @@ static const struct of_device_id axmclk_match_table[] = { }; MODULE_DEVICE_TABLE(of, axmclk_match_table); -struct axmclk_priv { - struct clk_onecell_data onecell; - struct clk *clks[]; -}; - static int axmclk_probe(struct platform_device *pdev) { void __iomem *base; struct resource *res; int i, ret; struct device *dev = &pdev->dev; - struct clk *clk; struct regmap *regmap; size_t num_clks; - struct axmclk_priv *priv; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, res); @@ -557,29 +563,18 @@ static int axmclk_probe(struct platform_device *pdev) num_clks = ARRAY_SIZE(axmclk_clocks); pr_info("axmclk: supporting %zu clocks\n", num_clks); - priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks, - GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->onecell.clks = priv->clks; - priv->onecell.clk_num = num_clks; /* Update each entry with the allocated regmap and register the clock * with the common clock framework */ for (i = 0; i < num_clks; i++) { axmclk_clocks[i]->regmap = regmap; - clk = devm_clk_register(dev, &axmclk_clocks[i]->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); - priv->clks[i] = clk; + ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw); + if (ret) + return ret; } - ret = of_clk_add_provider(dev->of_node, - of_clk_src_onecell_get, &priv->onecell); - - return ret; + return of_clk_add_hw_provider(dev->of_node, of_clk_axmclk_get, NULL); } static int axmclk_remove(struct platform_device *pdev) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index 01877f6..f21d909 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -71,7 +71,6 @@ struct cdce706_hw_data { struct cdce706_dev_data *dev_data; unsigned idx; unsigned parent; - struct clk *clk; struct clk_hw hw; unsigned div; unsigned mul; @@ -81,8 +80,6 @@ struct cdce706_hw_data { struct cdce706_dev_data { struct i2c_client *client; struct regmap *regmap; - struct clk_onecell_data onecell; - struct clk *clks[6]; struct clk *clkin_clk[2]; const char *clkin_name[2]; struct cdce706_hw_data clkin[1]; @@ -455,18 +452,19 @@ static int cdce706_register_hw(struct cdce706_dev_data *cdce, struct clk_init_data *init) { unsigned i; + int ret; for (i = 0; i < num_hw; ++i, ++hw) { init->name = clk_names[i]; hw->dev_data = cdce; hw->idx = i; hw->hw.init = init; - hw->clk = devm_clk_register(&cdce->client->dev, + ret = devm_clk_hw_register(&cdce->client->dev, &hw->hw); - if (IS_ERR(hw->clk)) { + if (ret) { dev_err(&cdce->client->dev, "Failed to register %s\n", clk_names[i]); - return PTR_ERR(hw->clk); + return ret; } } return 0; @@ -613,13 +611,23 @@ static int cdce706_register_clkouts(struct cdce706_dev_data *cdce) cdce->clkout[i].parent); } - ret = cdce706_register_hw(cdce, cdce->clkout, - ARRAY_SIZE(cdce->clkout), - cdce706_clkout_name, &init); - for (i = 0; i < ARRAY_SIZE(cdce->clkout); ++i) - cdce->clks[i] = cdce->clkout[i].clk; + return cdce706_register_hw(cdce, cdce->clkout, + ARRAY_SIZE(cdce->clkout), + cdce706_clkout_name, &init); +} - return ret; +static struct clk_hw * +of_clk_cdce_get(struct of_phandle_args *clkspec, void *data) +{ + struct cdce706_dev_data *cdce = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= ARRAY_SIZE(cdce->clkout)) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &cdce->clkout[idx].hw; } static int cdce706_probe(struct i2c_client *client, @@ -657,12 +665,8 @@ static int cdce706_probe(struct i2c_client *client, ret = cdce706_register_clkouts(cdce); if (ret < 0) return ret; - cdce->onecell.clks = cdce->clks; - cdce->onecell.clk_num = ARRAY_SIZE(cdce->clks); - ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, - &cdce->onecell); - - return ret; + return of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce_get, + cdce); } static int cdce706_remove(struct i2c_client *client) diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index 089bf88..b8459c1 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -62,8 +62,6 @@ struct clk_cdce925_chip { struct i2c_client *i2c_client; struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; - struct clk *dt_clk[NUMBER_OF_OUTPUTS]; - struct clk_onecell_data onecell; }; /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ @@ -557,6 +555,20 @@ static int cdce925_regmap_i2c_read(void *context, return -EIO; } +static struct clk_hw * +of_clk_cdce925_get(struct of_phandle_args *clkspec, void *_data) +{ + struct clk_cdce925_chip *data = _data; + unsigned int idx = clkspec->args[0]; + + if (idx >= ARRAY_SIZE(data->clk)) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &data->clk[idx].hw; +} + /* The CDCE925 uses a funky way to read/write registers. Bulk mode is * just weird, so just use the single byte mode exclusively. */ static struct regmap_bus regmap_cdce925_bus = { @@ -572,7 +584,6 @@ static int cdce925_probe(struct i2c_client *client, const char *parent_name; const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,}; struct clk_init_data init; - struct clk *clk; u32 value; int i; int err; @@ -622,10 +633,9 @@ static int cdce925_probe(struct i2c_client *client, data->pll[i].chip = data; data->pll[i].hw.init = &init; data->pll[i].index = i; - clk = devm_clk_register(&client->dev, &data->pll[i].hw); - if (IS_ERR(clk)) { + err = devm_clk_hw_register(&client->dev, &data->pll[i].hw); + if (err) { dev_err(&client->dev, "Failed register PLL %d\n", i); - err = PTR_ERR(clk); goto error; } sprintf(child_name, "PLL%d", i+1); @@ -634,7 +644,7 @@ static int cdce925_probe(struct i2c_client *client, continue; if (!of_property_read_u32(np_output, "clock-frequency", &value)) { - err = clk_set_rate(clk, value); + err = clk_set_rate(data->pll[i].hw.clk, value); if (err) dev_err(&client->dev, "unable to set PLL frequency %ud\n", @@ -663,14 +673,12 @@ static int cdce925_probe(struct i2c_client *client, data->clk[0].hw.init = &init; data->clk[0].index = 0; data->clk[0].pdiv = 1; - clk = devm_clk_register(&client->dev, &data->clk[0].hw); + err = devm_clk_hw_register(&client->dev, &data->clk[0].hw); kfree(init.name); /* clock framework made a copy of the name */ - if (IS_ERR(clk)) { + if (err) { dev_err(&client->dev, "clock registration Y1 failed\n"); - err = PTR_ERR(clk); goto error; } - data->dt_clk[0] = clk; /* Register output clocks Y2 .. Y5*/ init.ops = &cdce925_clk_ops; @@ -695,21 +703,17 @@ static int cdce925_probe(struct i2c_client *client, init.parent_names = &pll_clk_name[1]; break; } - clk = devm_clk_register(&client->dev, &data->clk[i].hw); + err = devm_clk_hw_register(&client->dev, &data->clk[i].hw); kfree(init.name); /* clock framework made a copy of the name */ - if (IS_ERR(clk)) { + if (err) { dev_err(&client->dev, "clock registration failed\n"); - err = PTR_ERR(clk); goto error; } - data->dt_clk[i] = clk; } /* Register the output clocks */ - data->onecell.clk_num = NUMBER_OF_OUTPUTS; - data->onecell.clks = data->dt_clk; - err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, - &data->onecell); + err = of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce925_get, + data); if (err) dev_err(&client->dev, "unable to add OF clock provider\n"); diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index adaf109..9193f64 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -40,9 +40,8 @@ static const struct clk_div_table timer_div_table[] = { }; struct clps711x_clk { - struct clk_onecell_data clk_data; - spinlock_t lock; - struct clk *clks[CLPS711X_CLK_MAX]; + spinlock_t lock; + struct clk_hw_onecell_data clk_data; }; static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, @@ -55,7 +54,9 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, if (!base) return ERR_PTR(-ENOMEM); - clps711x_clk = kzalloc(sizeof(*clps711x_clk), GFP_KERNEL); + clps711x_clk = kzalloc(sizeof(*clps711x_clk) + + sizeof(*clps711x_clk->clk_data.hws) * CLPS711X_CLK_MAX, + GFP_KERNEL); if (!clps711x_clk) return ERR_PTR(-ENOMEM); @@ -106,40 +107,40 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, tmp |= SYSCON1_TC2M | SYSCON1_TC2S; writel(tmp, base + CLPS711X_SYSCON1); - clps711x_clk->clks[CLPS711X_CLK_DUMMY] = - clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); - clps711x_clk->clks[CLPS711X_CLK_CPU] = - clk_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu); - clps711x_clk->clks[CLPS711X_CLK_BUS] = - clk_register_fixed_rate(NULL, "bus", NULL, 0, f_bus); - clps711x_clk->clks[CLPS711X_CLK_PLL] = - clk_register_fixed_rate(NULL, "pll", NULL, 0, f_pll); - clps711x_clk->clks[CLPS711X_CLK_TIMERREF] = - clk_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim); - clps711x_clk->clks[CLPS711X_CLK_TIMER1] = - clk_register_divider_table(NULL, "timer1", "timer_ref", 0, + clps711x_clk->clk_data.hws[CLPS711X_CLK_DUMMY] = + clk_hw_register_fixed_rate(NULL, "dummy", NULL, 0, 0); + clps711x_clk->clk_data.hws[CLPS711X_CLK_CPU] = + clk_hw_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu); + clps711x_clk->clk_data.hws[CLPS711X_CLK_BUS] = + clk_hw_register_fixed_rate(NULL, "bus", NULL, 0, f_bus); + clps711x_clk->clk_data.hws[CLPS711X_CLK_PLL] = + clk_hw_register_fixed_rate(NULL, "pll", NULL, 0, f_pll); + clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMERREF] = + clk_hw_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim); + clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1] = + clk_hw_register_divider_table(NULL, "timer1", "timer_ref", 0, base + CLPS711X_SYSCON1, 5, 1, 0, timer_div_table, &clps711x_clk->lock); - clps711x_clk->clks[CLPS711X_CLK_TIMER2] = - clk_register_divider_table(NULL, "timer2", "timer_ref", 0, + clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2] = + clk_hw_register_divider_table(NULL, "timer2", "timer_ref", 0, base + CLPS711X_SYSCON1, 7, 1, 0, timer_div_table, &clps711x_clk->lock); - clps711x_clk->clks[CLPS711X_CLK_PWM] = - clk_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm); - clps711x_clk->clks[CLPS711X_CLK_SPIREF] = - clk_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi); - clps711x_clk->clks[CLPS711X_CLK_SPI] = - clk_register_divider_table(NULL, "spi", "spi_ref", 0, + clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM] = + clk_hw_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm); + clps711x_clk->clk_data.hws[CLPS711X_CLK_SPIREF] = + clk_hw_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi); + clps711x_clk->clk_data.hws[CLPS711X_CLK_SPI] = + clk_hw_register_divider_table(NULL, "spi", "spi_ref", 0, base + CLPS711X_SYSCON1, 16, 2, 0, spi_div_table, &clps711x_clk->lock); - clps711x_clk->clks[CLPS711X_CLK_UART] = - clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); - clps711x_clk->clks[CLPS711X_CLK_TICK] = - clk_register_fixed_rate(NULL, "tick", NULL, 0, 64); + clps711x_clk->clk_data.hws[CLPS711X_CLK_UART] = + clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); + clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] = + clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64); for (i = 0; i < CLPS711X_CLK_MAX; i++) - if (IS_ERR(clps711x_clk->clks[i])) + if (IS_ERR(clps711x_clk->clk_data.hws[i])) pr_err("clk %i: register failed with %ld\n", - i, PTR_ERR(clps711x_clk->clks[i])); + i, PTR_ERR(clps711x_clk->clk_data.hws[i])); return clps711x_clk; } @@ -153,17 +154,17 @@ void __init clps711x_clk_init(void __iomem *base) BUG_ON(IS_ERR(clps711x_clk)); /* Clocksource */ - clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER1], + clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1], NULL, "clps711x-timer.0"); - clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER2], + clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2], NULL, "clps711x-timer.1"); /* Drivers */ - clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_PWM], + clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM], NULL, "clps711x-pwm"); - clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], NULL, "clps711x-uart.0"); - clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], NULL, "clps711x-uart.1"); } @@ -179,10 +180,9 @@ static void __init clps711x_clk_init_dt(struct device_node *np) clps711x_clk = _clps711x_clk_init(base, fref); BUG_ON(IS_ERR(clps711x_clk)); - clps711x_clk->clk_data.clks = clps711x_clk->clks; - clps711x_clk->clk_data.clk_num = CLPS711X_CLK_MAX; - of_clk_add_provider(np, of_clk_src_onecell_get, - &clps711x_clk->clk_data); + clps711x_clk->clk_data.num = CLPS711X_CLK_MAX; + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &clps711x_clk->clk_data); } CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt); #endif diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index 7379de8..021f3da 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -59,7 +59,6 @@ struct cs2000_priv { struct i2c_client *client; struct clk *clk_in; struct clk *ref_clk; - struct clk *clk_out; }; static const struct of_device_id cs2000_of_match[] = { @@ -371,7 +370,6 @@ static int cs2000_clk_register(struct cs2000_priv *priv) struct device_node *np = dev->of_node; struct clk_init_data init; const char *name = np->name; - struct clk *clk; static const char *parent_names[CLK_MAX]; int ch = 0; /* it uses ch0 only at this point */ int rate; @@ -400,18 +398,16 @@ static int cs2000_clk_register(struct cs2000_priv *priv) priv->hw.init = &init; - clk = clk_register(dev, &priv->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = clk_hw_register(dev, &priv->hw); + if (ret) + return ret; - ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw); if (ret < 0) { - clk_unregister(clk); + clk_hw_unregister(&priv->hw); return ret; } - priv->clk_out = clk; - return 0; } @@ -454,7 +450,7 @@ static int cs2000_remove(struct i2c_client *client) of_clk_del_provider(np); - clk_unregister(priv->clk_out); + clk_hw_unregister(&priv->hw); return 0; } diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index a0f55bc..96386ff 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -352,7 +352,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, /* if read only, just return current value */ if (divider->flags & CLK_DIVIDER_READ_ONLY) { - bestdiv = readl(divider->reg) >> divider->shift; + bestdiv = clk_readl(divider->reg) >> divider->shift; bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags, divider->width); diff --git a/drivers/clk/clk-efm32gg.c b/drivers/clk/clk-efm32gg.c index 22e4c65..8802a2d 100644 --- a/drivers/clk/clk-efm32gg.c +++ b/drivers/clk/clk-efm32gg.c @@ -10,24 +10,31 @@ #include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/slab.h> #include <dt-bindings/clock/efm32-cmu.h> #define CMU_HFPERCLKEN0 0x44 +#define CMU_MAX_CLKS 37 -static struct clk *clk[37]; -static struct clk_onecell_data clk_data = { - .clks = clk, - .clk_num = ARRAY_SIZE(clk), -}; +static struct clk_hw_onecell_data *clk_data; static void __init efm32gg_cmu_init(struct device_node *np) { int i; void __iomem *base; + struct clk_hw **hws; - for (i = 0; i < ARRAY_SIZE(clk); ++i) - clk[i] = ERR_PTR(-ENOENT); + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * CMU_MAX_CLKS, GFP_KERNEL); + + if (!clk_data) + return; + + hws = clk_data->hws; + + for (i = 0; i < CMU_MAX_CLKS; ++i) + hws[i] = ERR_PTR(-ENOENT); base = of_iomap(np, 0); if (!base) { @@ -35,46 +42,46 @@ static void __init efm32gg_cmu_init(struct device_node *np) return; } - clk[clk_HFXO] = clk_register_fixed_rate(NULL, "HFXO", NULL, - 0, 48000000); + hws[clk_HFXO] = clk_hw_register_fixed_rate(NULL, "HFXO", NULL, 0, + 48000000); - clk[clk_HFPERCLKUSART0] = clk_register_gate(NULL, "HFPERCLK.USART0", + hws[clk_HFPERCLKUSART0] = clk_hw_register_gate(NULL, "HFPERCLK.USART0", "HFXO", 0, base + CMU_HFPERCLKEN0, 0, 0, NULL); - clk[clk_HFPERCLKUSART1] = clk_register_gate(NULL, "HFPERCLK.USART1", + hws[clk_HFPERCLKUSART1] = clk_hw_register_gate(NULL, "HFPERCLK.USART1", "HFXO", 0, base + CMU_HFPERCLKEN0, 1, 0, NULL); - clk[clk_HFPERCLKUSART2] = clk_register_gate(NULL, "HFPERCLK.USART2", + hws[clk_HFPERCLKUSART2] = clk_hw_register_gate(NULL, "HFPERCLK.USART2", "HFXO", 0, base + CMU_HFPERCLKEN0, 2, 0, NULL); - clk[clk_HFPERCLKUART0] = clk_register_gate(NULL, "HFPERCLK.UART0", + hws[clk_HFPERCLKUART0] = clk_hw_register_gate(NULL, "HFPERCLK.UART0", "HFXO", 0, base + CMU_HFPERCLKEN0, 3, 0, NULL); - clk[clk_HFPERCLKUART1] = clk_register_gate(NULL, "HFPERCLK.UART1", + hws[clk_HFPERCLKUART1] = clk_hw_register_gate(NULL, "HFPERCLK.UART1", "HFXO", 0, base + CMU_HFPERCLKEN0, 4, 0, NULL); - clk[clk_HFPERCLKTIMER0] = clk_register_gate(NULL, "HFPERCLK.TIMER0", + hws[clk_HFPERCLKTIMER0] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER0", "HFXO", 0, base + CMU_HFPERCLKEN0, 5, 0, NULL); - clk[clk_HFPERCLKTIMER1] = clk_register_gate(NULL, "HFPERCLK.TIMER1", + hws[clk_HFPERCLKTIMER1] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER1", "HFXO", 0, base + CMU_HFPERCLKEN0, 6, 0, NULL); - clk[clk_HFPERCLKTIMER2] = clk_register_gate(NULL, "HFPERCLK.TIMER2", + hws[clk_HFPERCLKTIMER2] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER2", "HFXO", 0, base + CMU_HFPERCLKEN0, 7, 0, NULL); - clk[clk_HFPERCLKTIMER3] = clk_register_gate(NULL, "HFPERCLK.TIMER3", + hws[clk_HFPERCLKTIMER3] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER3", "HFXO", 0, base + CMU_HFPERCLKEN0, 8, 0, NULL); - clk[clk_HFPERCLKACMP0] = clk_register_gate(NULL, "HFPERCLK.ACMP0", + hws[clk_HFPERCLKACMP0] = clk_hw_register_gate(NULL, "HFPERCLK.ACMP0", "HFXO", 0, base + CMU_HFPERCLKEN0, 9, 0, NULL); - clk[clk_HFPERCLKACMP1] = clk_register_gate(NULL, "HFPERCLK.ACMP1", + hws[clk_HFPERCLKACMP1] = clk_hw_register_gate(NULL, "HFPERCLK.ACMP1", "HFXO", 0, base + CMU_HFPERCLKEN0, 10, 0, NULL); - clk[clk_HFPERCLKI2C0] = clk_register_gate(NULL, "HFPERCLK.I2C0", + hws[clk_HFPERCLKI2C0] = clk_hw_register_gate(NULL, "HFPERCLK.I2C0", "HFXO", 0, base + CMU_HFPERCLKEN0, 11, 0, NULL); - clk[clk_HFPERCLKI2C1] = clk_register_gate(NULL, "HFPERCLK.I2C1", + hws[clk_HFPERCLKI2C1] = clk_hw_register_gate(NULL, "HFPERCLK.I2C1", "HFXO", 0, base + CMU_HFPERCLKEN0, 12, 0, NULL); - clk[clk_HFPERCLKGPIO] = clk_register_gate(NULL, "HFPERCLK.GPIO", + hws[clk_HFPERCLKGPIO] = clk_hw_register_gate(NULL, "HFPERCLK.GPIO", "HFXO", 0, base + CMU_HFPERCLKEN0, 13, 0, NULL); - clk[clk_HFPERCLKVCMP] = clk_register_gate(NULL, "HFPERCLK.VCMP", + hws[clk_HFPERCLKVCMP] = clk_hw_register_gate(NULL, "HFPERCLK.VCMP", "HFXO", 0, base + CMU_HFPERCLKEN0, 14, 0, NULL); - clk[clk_HFPERCLKPRS] = clk_register_gate(NULL, "HFPERCLK.PRS", + hws[clk_HFPERCLKPRS] = clk_hw_register_gate(NULL, "HFPERCLK.PRS", "HFXO", 0, base + CMU_HFPERCLKEN0, 15, 0, NULL); - clk[clk_HFPERCLKADC0] = clk_register_gate(NULL, "HFPERCLK.ADC0", + hws[clk_HFPERCLKADC0] = clk_hw_register_gate(NULL, "HFPERCLK.ADC0", "HFXO", 0, base + CMU_HFPERCLKEN0, 16, 0, NULL); - clk[clk_HFPERCLKDAC0] = clk_register_gate(NULL, "HFPERCLK.DAC0", + hws[clk_HFPERCLKDAC0] = clk_hw_register_gate(NULL, "HFPERCLK.DAC0", "HFXO", 0, base + CMU_HFPERCLKEN0, 17, 0, NULL); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data); } CLK_OF_DECLARE(efm32ggcmu, "efm32gg,cmu", efm32gg_cmu_init); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 4db3be2..a5d402d 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/platform_device.h> /* * DOC: basic fixed multiplier and divider clock that cannot gate @@ -147,27 +148,25 @@ static const struct of_device_id set_rate_parent_matches[] = { { /* Sentinel */ }, }; -/** - * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock - */ -void __init of_fixed_factor_clk_setup(struct device_node *node) +static struct clk *_of_fixed_factor_clk_setup(struct device_node *node) { struct clk *clk; const char *clk_name = node->name; const char *parent_name; unsigned long flags = 0; u32 div, mult; + int ret; if (of_property_read_u32(node, "clock-div", &div)) { pr_err("%s Fixed factor clock <%s> must have a clock-div property\n", __func__, node->name); - return; + return ERR_PTR(-EIO); } if (of_property_read_u32(node, "clock-mult", &mult)) { pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n", __func__, node->name); - return; + return ERR_PTR(-EIO); } of_property_read_string(node, "clock-output-names", &clk_name); @@ -178,10 +177,67 @@ void __init of_fixed_factor_clk_setup(struct device_node *node) clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags, mult, div); - if (!IS_ERR(clk)) - of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (IS_ERR(clk)) + return clk; + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + clk_unregister(clk); + return ERR_PTR(ret); + } + + return clk; +} + +/** + * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock + */ +void __init of_fixed_factor_clk_setup(struct device_node *node) +{ + _of_fixed_factor_clk_setup(node); } -EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup); CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", of_fixed_factor_clk_setup); + +static int of_fixed_factor_clk_remove(struct platform_device *pdev) +{ + struct clk *clk = platform_get_drvdata(pdev); + + clk_unregister_fixed_factor(clk); + + return 0; +} + +static int of_fixed_factor_clk_probe(struct platform_device *pdev) +{ + struct clk *clk; + + /* + * This function is not executed when of_fixed_factor_clk_setup + * succeeded. + */ + clk = _of_fixed_factor_clk_setup(pdev->dev.of_node); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + platform_set_drvdata(pdev, clk); + + return 0; +} + +static const struct of_device_id of_fixed_factor_clk_ids[] = { + { .compatible = "fixed-factor-clock" }, + { } +}; +MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids); + +static struct platform_driver of_fixed_factor_clk_driver = { + .driver = { + .name = "of_fixed_factor_clk", + .of_match_table = of_fixed_factor_clk_ids, + }, + .probe = of_fixed_factor_clk_probe, + .remove = of_fixed_factor_clk_remove, +}; +builtin_platform_driver(of_fixed_factor_clk_driver); #endif diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 2edb393..b5c46b3 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/platform_device.h> /* * DOC: basic fixed-rate clock that cannot gate @@ -157,18 +158,16 @@ void clk_hw_unregister_fixed_rate(struct clk_hw *hw) EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_rate); #ifdef CONFIG_OF -/** - * of_fixed_clk_setup() - Setup function for simple fixed rate clock - */ -void of_fixed_clk_setup(struct device_node *node) +static struct clk *_of_fixed_clk_setup(struct device_node *node) { struct clk *clk; const char *clk_name = node->name; u32 rate; u32 accuracy = 0; + int ret; if (of_property_read_u32(node, "clock-frequency", &rate)) - return; + return ERR_PTR(-EIO); of_property_read_u32(node, "clock-accuracy", &accuracy); @@ -176,9 +175,66 @@ void of_fixed_clk_setup(struct device_node *node) clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL, 0, rate, accuracy); - if (!IS_ERR(clk)) - of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (IS_ERR(clk)) + return clk; + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + clk_unregister(clk); + return ERR_PTR(ret); + } + + return clk; +} + +/** + * of_fixed_clk_setup() - Setup function for simple fixed rate clock + */ +void __init of_fixed_clk_setup(struct device_node *node) +{ + _of_fixed_clk_setup(node); } -EXPORT_SYMBOL_GPL(of_fixed_clk_setup); CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup); + +static int of_fixed_clk_remove(struct platform_device *pdev) +{ + struct clk *clk = platform_get_drvdata(pdev); + + clk_unregister_fixed_rate(clk); + + return 0; +} + +static int of_fixed_clk_probe(struct platform_device *pdev) +{ + struct clk *clk; + + /* + * This function is not executed when of_fixed_clk_setup + * succeeded. + */ + clk = _of_fixed_clk_setup(pdev->dev.of_node); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + platform_set_drvdata(pdev, clk); + + return 0; +} + +static const struct of_device_id of_fixed_clk_ids[] = { + { .compatible = "fixed-clock" }, + { } +}; +MODULE_DEVICE_TABLE(of, of_fixed_clk_ids); + +static struct platform_driver of_fixed_clk_driver = { + .driver = { + .name = "of_fixed_clk", + .of_match_table = of_fixed_clk_ids, + }, + .probe = of_fixed_clk_probe, + .remove = of_fixed_clk_remove, +}; +builtin_platform_driver(of_fixed_clk_driver); #endif diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c deleted file mode 100644 index 5097831..0000000 --- a/drivers/clk/clk-ls1x.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com> - * - * 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 <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/err.h> - -#include <loongson1.h> - -#define OSC (33 * 1000000) -#define DIV_APB 2 - -static DEFINE_SPINLOCK(_lock); - -static int ls1x_pll_clk_enable(struct clk_hw *hw) -{ - return 0; -} - -static void ls1x_pll_clk_disable(struct clk_hw *hw) -{ -} - -static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - u32 pll, rate; - - pll = __raw_readl(LS1X_CLK_PLL_FREQ); - rate = 12 + (pll & 0x3f) + (((pll >> 8) & 0x3ff) >> 10); - rate *= OSC; - rate >>= 1; - - return rate; -} - -static const struct clk_ops ls1x_pll_clk_ops = { - .enable = ls1x_pll_clk_enable, - .disable = ls1x_pll_clk_disable, - .recalc_rate = ls1x_pll_recalc_rate, -}; - -static struct clk *__init clk_register_pll(struct device *dev, - const char *name, - const char *parent_name, - unsigned long flags) -{ - struct clk_hw *hw; - struct clk *clk; - struct clk_init_data init; - - /* allocate the divider */ - hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL); - if (!hw) { - pr_err("%s: could not allocate clk_hw\n", __func__); - return ERR_PTR(-ENOMEM); - } - - init.name = name; - init.ops = &ls1x_pll_clk_ops; - init.flags = flags | CLK_IS_BASIC; - init.parent_names = (parent_name ? &parent_name : NULL); - init.num_parents = (parent_name ? 1 : 0); - hw->init = &init; - - /* register the clock */ - clk = clk_register(dev, hw); - - if (IS_ERR(clk)) - kfree(hw); - - return clk; -} - -static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; -static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; -static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; - -void __init ls1x_clk_init(void) -{ - struct clk *clk; - - clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, 0, OSC); - clk_register_clkdev(clk, "osc_33m_clk", NULL); - - /* clock derived from 33 MHz OSC clk */ - clk = clk_register_pll(NULL, "pll_clk", "osc_33m_clk", 0); - clk_register_clkdev(clk, "pll_clk", NULL); - - /* clock derived from PLL clk */ - /* _____ - * _______________________| | - * OSC ___/ | MUX |___ CPU CLK - * \___ PLL ___ CPU DIV ___| | - * |_____| - */ - clk = clk_register_divider(NULL, "cpu_clk_div", "pll_clk", - CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV, - DIV_CPU_SHIFT, DIV_CPU_WIDTH, - CLK_DIVIDER_ONE_BASED | - CLK_DIVIDER_ROUND_CLOSEST, &_lock); - clk_register_clkdev(clk, "cpu_clk_div", NULL); - clk = clk_register_mux(NULL, "cpu_clk", cpu_parents, - ARRAY_SIZE(cpu_parents), - CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, - BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock); - clk_register_clkdev(clk, "cpu_clk", NULL); - - /* _____ - * _______________________| | - * OSC ___/ | MUX |___ DC CLK - * \___ PLL ___ DC DIV ___| | - * |_____| - */ - clk = clk_register_divider(NULL, "dc_clk_div", "pll_clk", - 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, - DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); - clk_register_clkdev(clk, "dc_clk_div", NULL); - clk = clk_register_mux(NULL, "dc_clk", dc_parents, - ARRAY_SIZE(dc_parents), - CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, - BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock); - clk_register_clkdev(clk, "dc_clk", NULL); - - /* _____ - * _______________________| | - * OSC ___/ | MUX |___ DDR CLK - * \___ PLL ___ DDR DIV ___| | - * |_____| - */ - clk = clk_register_divider(NULL, "ahb_clk_div", "pll_clk", - 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT, - DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, - &_lock); - clk_register_clkdev(clk, "ahb_clk_div", NULL); - clk = clk_register_mux(NULL, "ahb_clk", ahb_parents, - ARRAY_SIZE(ahb_parents), - CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, - BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock); - clk_register_clkdev(clk, "ahb_clk", NULL); - clk_register_clkdev(clk, "stmmaceth", NULL); - - /* clock derived from AHB clk */ - /* APB clk is always half of the AHB clk */ - clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, - DIV_APB); - clk_register_clkdev(clk, "apb_clk", NULL); - clk_register_clkdev(clk, "ls1x_i2c", NULL); - clk_register_clkdev(clk, "ls1x_pwmtimer", NULL); - clk_register_clkdev(clk, "ls1x_spi", NULL); - clk_register_clkdev(clk, "ls1x_wdt", NULL); - clk_register_clkdev(clk, "serial8250", NULL); -} diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c deleted file mode 100644 index 35af9cb..0000000 --- a/drivers/clk/clk-max-gen.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks - * - * Copyright (C) 2014 Google, Inc - * - * Copyright (C) 2012 Samsung Electornics - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * - * 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. - * - * This driver is based on clk-max77686.c - * - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/regmap.h> -#include <linux/platform_device.h> -#include <linux/clk-provider.h> -#include <linux/mutex.h> -#include <linux/clkdev.h> -#include <linux/of.h> -#include <linux/export.h> - -#include "clk-max-gen.h" - -struct max_gen_clk { - struct regmap *regmap; - u32 mask; - u32 reg; - struct clk_hw hw; -}; - -static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw) -{ - return container_of(hw, struct max_gen_clk, hw); -} - -static int max_gen_clk_prepare(struct clk_hw *hw) -{ - struct max_gen_clk *max_gen = to_max_gen_clk(hw); - - return regmap_update_bits(max_gen->regmap, max_gen->reg, - max_gen->mask, max_gen->mask); -} - -static void max_gen_clk_unprepare(struct clk_hw *hw) -{ - struct max_gen_clk *max_gen = to_max_gen_clk(hw); - - regmap_update_bits(max_gen->regmap, max_gen->reg, - max_gen->mask, ~max_gen->mask); -} - -static int max_gen_clk_is_prepared(struct clk_hw *hw) -{ - struct max_gen_clk *max_gen = to_max_gen_clk(hw); - int ret; - u32 val; - - ret = regmap_read(max_gen->regmap, max_gen->reg, &val); - - if (ret < 0) - return -EINVAL; - - return val & max_gen->mask; -} - -static unsigned long max_gen_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 32768; -} - -struct clk_ops max_gen_clk_ops = { - .prepare = max_gen_clk_prepare, - .unprepare = max_gen_clk_unprepare, - .is_prepared = max_gen_clk_is_prepared, - .recalc_rate = max_gen_recalc_rate, -}; -EXPORT_SYMBOL_GPL(max_gen_clk_ops); - -static struct clk *max_gen_clk_register(struct device *dev, - struct max_gen_clk *max_gen) -{ - struct clk *clk; - struct clk_hw *hw = &max_gen->hw; - int ret; - - clk = devm_clk_register(dev, hw); - if (IS_ERR(clk)) - return clk; - - ret = clk_register_clkdev(clk, hw->init->name, NULL); - - if (ret) - return ERR_PTR(ret); - - return clk; -} - -int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, - u32 reg, struct clk_init_data *clks_init, int num_init) -{ - int i, ret; - struct max_gen_clk *max_gen_clks; - struct clk **clocks; - struct device *dev = pdev->dev.parent; - const char *clk_name; - struct clk_init_data *init; - - clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL); - if (!clocks) - return -ENOMEM; - - max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk) - * num_init, GFP_KERNEL); - if (!max_gen_clks) - return -ENOMEM; - - for (i = 0; i < num_init; i++) { - max_gen_clks[i].regmap = regmap; - max_gen_clks[i].mask = 1 << i; - max_gen_clks[i].reg = reg; - - init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL); - if (!init) - return -ENOMEM; - - if (dev->of_node && - !of_property_read_string_index(dev->of_node, - "clock-output-names", - i, &clk_name)) - init->name = clk_name; - else - init->name = clks_init[i].name; - - init->ops = clks_init[i].ops; - init->flags = clks_init[i].flags; - - max_gen_clks[i].hw.init = init; - - clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]); - if (IS_ERR(clocks[i])) { - ret = PTR_ERR(clocks[i]); - dev_err(dev, "failed to register %s\n", - max_gen_clks[i].hw.init->name); - return ret; - } - } - - platform_set_drvdata(pdev, clocks); - - if (dev->of_node) { - struct clk_onecell_data *of_data; - - of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL); - if (!of_data) - return -ENOMEM; - - of_data->clks = clocks; - of_data->clk_num = num_init; - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, - of_data); - - if (ret) { - dev_err(dev, "failed to register OF clock provider\n"); - return ret; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(max_gen_clk_probe); - -int max_gen_clk_remove(struct platform_device *pdev, int num_init) -{ - struct device *dev = pdev->dev.parent; - - if (dev->of_node) - of_clk_del_provider(dev->of_node); - - return 0; -} -EXPORT_SYMBOL_GPL(max_gen_clk_remove); diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h deleted file mode 100644 index 997e86f..0000000 --- a/drivers/clk/clk-max-gen.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks - * - * Copyright (C) 2014 Google, Inc - * - * 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 __CLK_MAX_GEN_H__ -#define __CLK_MAX_GEN_H__ - -#include <linux/types.h> -#include <linux/device.h> -#include <linux/clkdev.h> -#include <linux/regmap.h> -#include <linux/platform_device.h> - -int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, - u32 reg, struct clk_init_data *clks_init, int num_init); -int max_gen_clk_remove(struct platform_device *pdev, int num_init); -extern struct clk_ops max_gen_clk_ops; - -#endif /* __CLK_MAX_GEN_H__ */ diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 9b6f277..b637f59 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -1,5 +1,5 @@ /* - * clk-max77686.c - Clock driver for Maxim 77686 + * clk-max77686.c - Clock driver for Maxim 77686/MAX77802 * * Copyright (C) 2012 Samsung Electornics * Jonghwa Lee <jonghwa3.lee@samsung.com> @@ -25,46 +25,284 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/mfd/max77620.h> #include <linux/mfd/max77686.h> #include <linux/mfd/max77686-private.h> #include <linux/clk-provider.h> #include <linux/mutex.h> #include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/regmap.h> #include <dt-bindings/clock/maxim,max77686.h> -#include "clk-max-gen.h" +#include <dt-bindings/clock/maxim,max77802.h> +#include <dt-bindings/clock/maxim,max77620.h> -static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { +#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 + +enum max77686_chip_name { + CHIP_MAX77686, + CHIP_MAX77802, + CHIP_MAX77620, +}; + +struct max77686_hw_clk_info { + const char *name; + u32 clk_reg; + u32 clk_enable_mask; + u32 flags; +}; + +struct max77686_clk_init_data { + struct regmap *regmap; + struct clk_hw hw; + struct clk_init_data clk_idata; + const struct max77686_hw_clk_info *clk_info; +}; + +struct max77686_clk_driver_data { + enum max77686_chip_name chip; + struct max77686_clk_init_data *max_clk_data; + size_t num_clks; +}; + +static const struct +max77686_hw_clk_info max77686_hw_clks_info[MAX77686_CLKS_NUM] = { [MAX77686_CLK_AP] = { .name = "32khz_ap", - .ops = &max_gen_clk_ops, + .clk_reg = MAX77686_REG_32KHZ, + .clk_enable_mask = BIT(MAX77686_CLK_AP), }, [MAX77686_CLK_CP] = { .name = "32khz_cp", - .ops = &max_gen_clk_ops, + .clk_reg = MAX77686_REG_32KHZ, + .clk_enable_mask = BIT(MAX77686_CLK_CP), }, [MAX77686_CLK_PMIC] = { .name = "32khz_pmic", - .ops = &max_gen_clk_ops, + .clk_reg = MAX77686_REG_32KHZ, + .clk_enable_mask = BIT(MAX77686_CLK_PMIC), + }, +}; + +static const struct +max77686_hw_clk_info max77802_hw_clks_info[MAX77802_CLKS_NUM] = { + [MAX77802_CLK_32K_AP] = { + .name = "32khz_ap", + .clk_reg = MAX77802_REG_32KHZ, + .clk_enable_mask = BIT(MAX77802_CLK_32K_AP), + }, + [MAX77802_CLK_32K_CP] = { + .name = "32khz_cp", + .clk_reg = MAX77802_REG_32KHZ, + .clk_enable_mask = BIT(MAX77802_CLK_32K_CP), + }, +}; + +static const struct +max77686_hw_clk_info max77620_hw_clks_info[MAX77620_CLKS_NUM] = { + [MAX77620_CLK_32K_OUT0] = { + .name = "32khz_out0", + .clk_reg = MAX77620_REG_CNFG1_32K, + .clk_enable_mask = MAX77620_CNFG1_32K_OUT0_EN, }, }; +static struct max77686_clk_init_data *to_max77686_clk_init_data( + struct clk_hw *hw) +{ + return container_of(hw, struct max77686_clk_init_data, hw); +} + +static int max77686_clk_prepare(struct clk_hw *hw) +{ + struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); + + return regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg, + max77686->clk_info->clk_enable_mask, + max77686->clk_info->clk_enable_mask); +} + +static void max77686_clk_unprepare(struct clk_hw *hw) +{ + struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); + + regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg, + max77686->clk_info->clk_enable_mask, + ~max77686->clk_info->clk_enable_mask); +} + +static int max77686_clk_is_prepared(struct clk_hw *hw) +{ + struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); + int ret; + u32 val; + + ret = regmap_read(max77686->regmap, max77686->clk_info->clk_reg, &val); + + if (ret < 0) + return -EINVAL; + + return val & max77686->clk_info->clk_enable_mask; +} + +static unsigned long max77686_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +static struct clk_ops max77686_clk_ops = { + .prepare = max77686_clk_prepare, + .unprepare = max77686_clk_unprepare, + .is_prepared = max77686_clk_is_prepared, + .recalc_rate = max77686_recalc_rate, +}; + +static struct clk_hw * +of_clk_max77686_get(struct of_phandle_args *clkspec, void *data) +{ + struct max77686_clk_driver_data *drv_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= drv_data->num_clks) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &drv_data->max_clk_data[idx].hw; +} + static int max77686_clk_probe(struct platform_device *pdev) { - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + const struct platform_device_id *id = platform_get_device_id(pdev); + struct max77686_clk_driver_data *drv_data; + const struct max77686_hw_clk_info *hw_clks; + struct regmap *regmap; + int i, ret, num_clks; + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + regmap = dev_get_regmap(parent, NULL); + if (!regmap) { + dev_err(dev, "Failed to get rtc regmap\n"); + return -ENODEV; + } + + drv_data->chip = id->driver_data; + + switch (drv_data->chip) { + case CHIP_MAX77686: + num_clks = MAX77686_CLKS_NUM; + hw_clks = max77686_hw_clks_info; + break; + + case CHIP_MAX77802: + num_clks = MAX77802_CLKS_NUM; + hw_clks = max77802_hw_clks_info; + break; + + case CHIP_MAX77620: + num_clks = MAX77620_CLKS_NUM; + hw_clks = max77620_hw_clks_info; + break; - return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ, - max77686_clks_init, MAX77686_CLKS_NUM); + default: + dev_err(dev, "Unknown Chip ID\n"); + return -EINVAL; + } + + drv_data->max_clk_data = devm_kcalloc(dev, num_clks, + sizeof(*drv_data->max_clk_data), + GFP_KERNEL); + if (!drv_data->max_clk_data) + return -ENOMEM; + + for (i = 0; i < num_clks; i++) { + struct max77686_clk_init_data *max_clk_data; + const char *clk_name; + + max_clk_data = &drv_data->max_clk_data[i]; + + max_clk_data->regmap = regmap; + max_clk_data->clk_info = &hw_clks[i]; + max_clk_data->clk_idata.flags = hw_clks[i].flags; + max_clk_data->clk_idata.ops = &max77686_clk_ops; + + if (parent->of_node && + !of_property_read_string_index(parent->of_node, + "clock-output-names", + i, &clk_name)) + max_clk_data->clk_idata.name = clk_name; + else + max_clk_data->clk_idata.name = hw_clks[i].name; + + max_clk_data->hw.init = &max_clk_data->clk_idata; + + ret = devm_clk_hw_register(dev, &max_clk_data->hw); + if (ret) { + dev_err(dev, "Failed to clock register: %d\n", ret); + return ret; + } + + ret = clk_hw_register_clkdev(&max_clk_data->hw, + max_clk_data->clk_idata.name, NULL); + if (ret < 0) { + dev_err(dev, "Failed to clkdev register: %d\n", ret); + return ret; + } + } + + if (parent->of_node) { + ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get, + drv_data); + + if (ret < 0) { + dev_err(dev, "Failed to register OF clock provider: %d\n", + ret); + return ret; + } + } + + /* MAX77802: Enable low-jitter mode on the 32khz clocks. */ + if (drv_data->chip == CHIP_MAX77802) { + ret = regmap_update_bits(regmap, MAX77802_REG_32KHZ, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); + if (ret < 0) { + dev_err(dev, "Failed to config low-jitter: %d\n", ret); + goto remove_of_clk_provider; + } + } + + return 0; + +remove_of_clk_provider: + if (parent->of_node) + of_clk_del_provider(parent->of_node); + + return ret; } static int max77686_clk_remove(struct platform_device *pdev) { - return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM); + struct device *parent = pdev->dev.parent; + + if (parent->of_node) + of_clk_del_provider(parent->of_node); + + return 0; } static const struct platform_device_id max77686_clk_id[] = { - { "max77686-clk", 0}, - { }, + { "max77686-clk", .driver_data = CHIP_MAX77686, }, + { "max77802-clk", .driver_data = CHIP_MAX77802, }, + { "max77620-clock", .driver_data = CHIP_MAX77620, }, + {}, }; MODULE_DEVICE_TABLE(platform, max77686_clk_id); diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c deleted file mode 100644 index 355dd2e..0000000 --- a/drivers/clk/clk-max77802.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * clk-max77802.c - Clock driver for Maxim 77802 - * - * Copyright (C) 2014 Google, Inc - * - * Copyright (C) 2012 Samsung Electornics - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * - * 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. - * - * This driver is based on clk-max77686.c - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/mfd/max77686-private.h> -#include <linux/clk-provider.h> -#include <linux/mutex.h> -#include <linux/clkdev.h> - -#include <dt-bindings/clock/maxim,max77802.h> -#include "clk-max-gen.h" - -#define MAX77802_CLOCK_OPMODE_MASK 0x1 -#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 - -static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = { - [MAX77802_CLK_32K_AP] = { - .name = "32khz_ap", - .ops = &max_gen_clk_ops, - }, - [MAX77802_CLK_32K_CP] = { - .name = "32khz_cp", - .ops = &max_gen_clk_ops, - }, -}; - -static int max77802_clk_probe(struct platform_device *pdev) -{ - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - int ret; - - ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ, - max77802_clks_init, MAX77802_CLKS_NUM); - - if (ret) { - dev_err(&pdev->dev, "generic probe failed %d\n", ret); - return ret; - } - - /* Enable low-jitter mode on the 32khz clocks. */ - ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ, - 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, - 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); - if (ret < 0) - dev_err(&pdev->dev, "failed to enable low-jitter mode\n"); - - return ret; -} - -static int max77802_clk_remove(struct platform_device *pdev) -{ - return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM); -} - -static const struct platform_device_id max77802_clk_id[] = { - { "max77802-clk", 0}, - { }, -}; -MODULE_DEVICE_TABLE(platform, max77802_clk_id); - -static struct platform_driver max77802_clk_driver = { - .driver = { - .name = "max77802-clk", - }, - .probe = max77802_clk_probe, - .remove = max77802_clk_remove, - .id_table = max77802_clk_id, -}; - -module_platform_driver(max77802_clk_driver); - -MODULE_DESCRIPTION("MAXIM 77802 Clock Driver"); -MODULE_AUTHOR("Javier Martinez Canillas <javier@osg.samsung.com"); -MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c index e081775..2a83a3ff 100644 --- a/drivers/clk/clk-mb86s7x.c +++ b/drivers/clk/clk-mb86s7x.c @@ -327,10 +327,11 @@ static struct clk_ops clk_clc_ops = { .set_rate = clc_set_rate, }; -struct clk *mb86s7x_clclk_register(struct device *cpu_dev) +static struct clk_hw *mb86s7x_clclk_register(struct device *cpu_dev) { struct clk_init_data init; struct cl_clk *clc; + int ret; clc = kzalloc(sizeof(*clc), GFP_KERNEL); if (!clc) @@ -344,14 +345,17 @@ struct clk *mb86s7x_clclk_register(struct device *cpu_dev) init.flags = CLK_GET_RATE_NOCACHE; init.num_parents = 0; - return devm_clk_register(cpu_dev, &clc->hw); + ret = devm_clk_hw_register(cpu_dev, &clc->hw); + if (ret) + return ERR_PTR(ret); + return &clc->hw; } static int mb86s7x_clclk_of_init(void) { int cpu, ret = -ENODEV; struct device_node *np; - struct clk *clk; + struct clk_hw *hw; np = of_find_compatible_node(NULL, NULL, "fujitsu,mb86s70-scb-1.0"); if (!np || !of_device_is_available(np)) @@ -365,12 +369,12 @@ static int mb86s7x_clclk_of_init(void) continue; } - clk = mb86s7x_clclk_register(cpu_dev); - if (IS_ERR(clk)) { + hw = mb86s7x_clclk_register(cpu_dev); + if (IS_ERR(hw)) { pr_err("failed to register cpu%d clock\n", cpu); continue; } - if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) { + if (clk_hw_register_clkdev(hw, NULL, dev_name(cpu_dev))) { pr_err("failed to register cpu%d clock lookup\n", cpu); continue; } diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index f37f719..b86dac8 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -19,7 +19,8 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) { static void __iomem *base; - struct clk *clk, *ref_clk; + struct clk_hw *hw; + struct clk *ref_clk; unsigned int mul; const char *name = node->name; const char *parent_name; @@ -42,14 +43,14 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) return; } - clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); - if (IS_ERR(clk)) { + hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); + if (IS_ERR(hw)) { pr_err("%s: failed to register clock\n", node->full_name); return; } - clk_register_clkdev(clk, NULL, name); - of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_hw_register_clkdev(hw, NULL, name); + of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", moxart_of_pll_clk_init); @@ -57,7 +58,8 @@ CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", static void __init moxart_of_apb_clk_init(struct device_node *node) { static void __iomem *base; - struct clk *clk, *pll_clk; + struct clk_hw *hw; + struct clk *pll_clk; unsigned int div, val; unsigned int div_idx[] = { 2, 3, 4, 6, 8}; const char *name = node->name; @@ -85,14 +87,14 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) return; } - clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div); - if (IS_ERR(clk)) { + hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div); + if (IS_ERR(hw)) { pr_err("%s: failed to register clock\n", node->full_name); return; } - clk_register_clkdev(clk, NULL, name); - of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_hw_register_clkdev(hw, NULL, name); + of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); } CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock", moxart_of_apb_clk_init); diff --git a/drivers/clk/clk-nspire.c b/drivers/clk/clk-nspire.c index 64f196a..f861011 100644 --- a/drivers/clk/clk-nspire.c +++ b/drivers/clk/clk-nspire.c @@ -69,7 +69,7 @@ static void __init nspire_ahbdiv_setup(struct device_node *node, { u32 val; void __iomem *io; - struct clk *clk; + struct clk_hw *hw; const char *clk_name = node->name; const char *parent_name; struct nspire_clk_info info; @@ -85,10 +85,10 @@ static void __init nspire_ahbdiv_setup(struct device_node *node, of_property_read_string(node, "clock-output-names", &clk_name); parent_name = of_clk_get_parent_name(node, 0); - clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, - 1, info.base_ahb_ratio); - if (!IS_ERR(clk)) - of_clk_add_provider(node, of_clk_src_simple_get, clk); + hw = clk_hw_register_fixed_factor(NULL, clk_name, parent_name, 0, + 1, info.base_ahb_ratio); + if (!IS_ERR(hw)) + of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); } static void __init nspire_ahbdiv_setup_cx(struct device_node *node) @@ -111,7 +111,7 @@ static void __init nspire_clk_setup(struct device_node *node, { u32 val; void __iomem *io; - struct clk *clk; + struct clk_hw *hw; const char *clk_name = node->name; struct nspire_clk_info info; @@ -125,9 +125,10 @@ static void __init nspire_clk_setup(struct device_node *node, of_property_read_string(node, "clock-output-names", &clk_name); - clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, info.base_clock); - if (!IS_ERR(clk)) - of_clk_add_provider(node, of_clk_src_simple_get, clk); + hw = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, + info.base_clock); + if (!IS_ERR(hw)) + of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); else return; diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 8328863..31f590c 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -41,7 +41,6 @@ struct palmas_clk32k_desc { struct palmas_clock_info { struct device *dev; - struct clk *clk; struct clk_hw hw; struct palmas *palmas; const struct palmas_clk32k_desc *clk_desc; @@ -218,7 +217,7 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) } if (cinfo->ext_control_pin) { - ret = clk_prepare(cinfo->clk); + ret = clk_prepare(cinfo->hw.clk); if (ret < 0) { dev_err(cinfo->dev, "Clock prep failed, %d\n", ret); return ret; @@ -242,7 +241,6 @@ static int palmas_clks_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; const struct palmas_clks_of_match_data *match_data; struct palmas_clock_info *cinfo; - struct clk *clk; int ret; match_data = of_device_get_match_data(&pdev->dev); @@ -261,22 +259,20 @@ static int palmas_clks_probe(struct platform_device *pdev) cinfo->clk_desc = &match_data->desc; cinfo->hw.init = &match_data->init; - clk = devm_clk_register(&pdev->dev, &cinfo->hw); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + ret = devm_clk_hw_register(&pdev->dev, &cinfo->hw); + if (ret) { dev_err(&pdev->dev, "Fail to register clock %s, %d\n", match_data->desc.clk_name, ret); return ret; } - cinfo->clk = clk; ret = palmas_clks_init_configure(cinfo); if (ret < 0) { dev_err(&pdev->dev, "Clock config failed, %d\n", ret); return ret; } - ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk); + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw); if (ret < 0) dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret); return ret; diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c index 1630a1f..8cb9d11 100644 --- a/drivers/clk/clk-pwm.c +++ b/drivers/clk/clk-pwm.c @@ -61,7 +61,6 @@ static int clk_pwm_probe(struct platform_device *pdev) struct pwm_device *pwm; struct pwm_args pargs; const char *clk_name; - struct clk *clk; int ret; clk_pwm = devm_kzalloc(&pdev->dev, sizeof(*clk_pwm), GFP_KERNEL); @@ -107,11 +106,11 @@ static int clk_pwm_probe(struct platform_device *pdev) clk_pwm->pwm = pwm; clk_pwm->hw.init = &init; - clk = devm_clk_register(&pdev->dev, &clk_pwm->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(&pdev->dev, &clk_pwm->hw); + if (ret) + return ret; - return of_clk_add_provider(node, of_clk_src_simple_get, clk); + return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw); } static int clk_pwm_remove(struct platform_device *pdev) diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 58566a17..20b1055 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -766,7 +766,11 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) if (!hwc) return NULL; - hwc->reg = cg->regs + 0x20 * idx; + if (cg->info.flags & CG_VER3) + hwc->reg = cg->regs + 0x70000 + 0x20 * idx; + else + hwc->reg = cg->regs + 0x20 * idx; + hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]]; /* diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c index 7438303..6461f28 100644 --- a/drivers/clk/clk-rk808.c +++ b/drivers/clk/clk-rk808.c @@ -22,11 +22,8 @@ #include <linux/mfd/rk808.h> #include <linux/i2c.h> -#define RK808_NR_OUTPUT 2 - struct rk808_clkout { struct rk808 *rk808; - struct clk_onecell_data clk_data; struct clk_hw clkout1_hw; struct clk_hw clkout2_hw; }; @@ -85,14 +82,28 @@ static const struct clk_ops rk808_clkout2_ops = { .recalc_rate = rk808_clkout_recalc_rate, }; +static struct clk_hw * +of_clk_rk808_get(struct of_phandle_args *clkspec, void *data) +{ + struct rk808_clkout *rk808_clkout = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= 2) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return idx ? &rk808_clkout->clkout2_hw : &rk808_clkout->clkout1_hw; +} + static int rk808_clkout_probe(struct platform_device *pdev) { struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); struct i2c_client *client = rk808->i2c; struct device_node *node = client->dev.of_node; struct clk_init_data init = {}; - struct clk **clk_table; struct rk808_clkout *rk808_clkout; + int ret; rk808_clkout = devm_kzalloc(&client->dev, sizeof(*rk808_clkout), GFP_KERNEL); @@ -101,11 +112,6 @@ static int rk808_clkout_probe(struct platform_device *pdev) rk808_clkout->rk808 = rk808; - clk_table = devm_kcalloc(&client->dev, RK808_NR_OUTPUT, - sizeof(struct clk *), GFP_KERNEL); - if (!clk_table) - return -ENOMEM; - init.parent_names = NULL; init.num_parents = 0; init.name = "rk808-clkout1"; @@ -116,10 +122,9 @@ static int rk808_clkout_probe(struct platform_device *pdev) of_property_read_string_index(node, "clock-output-names", 0, &init.name); - clk_table[0] = devm_clk_register(&client->dev, - &rk808_clkout->clkout1_hw); - if (IS_ERR(clk_table[0])) - return PTR_ERR(clk_table[0]); + ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout1_hw); + if (ret) + return ret; init.name = "rk808-clkout2"; init.ops = &rk808_clkout2_ops; @@ -129,16 +134,11 @@ static int rk808_clkout_probe(struct platform_device *pdev) of_property_read_string_index(node, "clock-output-names", 1, &init.name); - clk_table[1] = devm_clk_register(&client->dev, - &rk808_clkout->clkout2_hw); - if (IS_ERR(clk_table[1])) - return PTR_ERR(clk_table[1]); - - rk808_clkout->clk_data.clks = clk_table; - rk808_clkout->clk_data.clk_num = RK808_NR_OUTPUT; + ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout2_hw); + if (ret) + return ret; - return of_clk_add_provider(node, of_clk_src_onecell_get, - &rk808_clkout->clk_data); + return of_clk_add_hw_provider(node, of_clk_rk808_get, rk808_clkout); } static int rk808_clkout_remove(struct platform_device *pdev) diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 6962ee5..2a3e9d8 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -146,13 +146,13 @@ static const struct of_device_id scpi_clk_match[] = { {} }; -static struct clk * +static int scpi_clk_ops_init(struct device *dev, const struct of_device_id *match, struct scpi_clk *sclk, const char *name) { struct clk_init_data init; - struct clk *clk; unsigned long min = 0, max = 0; + int ret; init.name = name; init.flags = 0; @@ -164,18 +164,18 @@ scpi_clk_ops_init(struct device *dev, const struct of_device_id *match, if (init.ops == &scpi_dvfs_ops) { sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id); if (IS_ERR(sclk->info)) - return NULL; + return PTR_ERR(sclk->info); } else if (init.ops == &scpi_clk_ops) { if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max) - return NULL; + return -EINVAL; } else { - return NULL; + return -EINVAL; } - clk = devm_clk_register(dev, &sclk->hw); - if (!IS_ERR(clk) && max) + ret = devm_clk_hw_register(dev, &sclk->hw); + if (!ret && max) clk_hw_set_rate_range(&sclk->hw, min, max); - return clk; + return ret; } struct scpi_clk_data { @@ -183,7 +183,7 @@ struct scpi_clk_data { unsigned int clk_num; }; -static struct clk * +static struct clk_hw * scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data) { struct scpi_clk *sclk; @@ -193,7 +193,7 @@ scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data) for (count = 0; count < clk_data->clk_num; count++) { sclk = clk_data->clk[count]; if (idx == sclk->id) - return sclk->hw.clk; + return &sclk->hw; } return ERR_PTR(-EINVAL); @@ -202,8 +202,7 @@ scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data) static int scpi_clk_add(struct device *dev, struct device_node *np, const struct of_device_id *match) { - struct clk **clks; - int idx, count; + int idx, count, err; struct scpi_clk_data *clk_data; count = of_property_count_strings(np, "clock-output-names"); @@ -222,10 +221,6 @@ static int scpi_clk_add(struct device *dev, struct device_node *np, if (!clk_data->clk) return -ENOMEM; - clks = devm_kcalloc(dev, count, sizeof(*clks), GFP_KERNEL); - if (!clks) - return -ENOMEM; - for (idx = 0; idx < count; idx++) { struct scpi_clk *sclk; const char *name; @@ -249,15 +244,15 @@ static int scpi_clk_add(struct device *dev, struct device_node *np, sclk->id = val; - clks[idx] = scpi_clk_ops_init(dev, match, sclk, name); - if (IS_ERR_OR_NULL(clks[idx])) + err = scpi_clk_ops_init(dev, match, sclk, name); + if (err) dev_err(dev, "failed to register clock '%s'\n", name); else dev_dbg(dev, "Registered clock '%s'\n", name); clk_data->clk[idx] = sclk; } - return of_clk_add_provider(np, scpi_of_clk_src_get, clk_data); + return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data); } static int scpi_clocks_remove(struct platform_device *pdev) diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c index ceef25b..09b6718 100644 --- a/drivers/clk/clk-si514.c +++ b/drivers/clk/clk-si514.c @@ -305,7 +305,6 @@ static int si514_probe(struct i2c_client *client, { struct clk_si514 *data; struct clk_init_data init; - struct clk *clk; int err; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); @@ -330,13 +329,13 @@ static int si514_probe(struct i2c_client *client, i2c_set_clientdata(client, data); - clk = devm_clk_register(&client->dev, &data->hw); - if (IS_ERR(clk)) { + err = devm_clk_hw_register(&client->dev, &data->hw); + if (err) { dev_err(&client->dev, "clock registration failed\n"); - return PTR_ERR(clk); + return err; } - err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get, - clk); + err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get, + &data->hw); if (err) { dev_err(&client->dev, "unable to add clk provider\n"); return err; diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index b1bc12c..b051db4 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -54,7 +54,6 @@ struct si5351_driver_data { enum si5351_variant variant; struct i2c_client *client; struct regmap *regmap; - struct clk_onecell_data onecell; struct clk *pxtal; const char *pxtal_name; @@ -66,6 +65,7 @@ struct si5351_driver_data { struct si5351_hw_data pll[2]; struct si5351_hw_data *msynth; struct si5351_hw_data *clkout; + size_t num_clkout; }; static const char * const si5351_input_names[] = { @@ -1307,11 +1307,31 @@ put_child: of_node_put(child); return -EINVAL; } + +static struct clk_hw * +si53351_of_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct si5351_driver_data *drvdata = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= drvdata->num_clkout) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &drvdata->clkout[idx].hw; +} #else static int si5351_dt_parse(struct i2c_client *client, enum si5351_variant variant) { return 0; } + +static struct clk_hw * +si53351_of_clk_get(struct of_phandle_args *clkspec, void *data) +{ + return NULL; +} #endif /* CONFIG_OF */ static int si5351_i2c_probe(struct i2c_client *client, @@ -1321,7 +1341,6 @@ static int si5351_i2c_probe(struct i2c_client *client, struct si5351_platform_data *pdata; struct si5351_driver_data *drvdata; struct clk_init_data init; - struct clk *clk; const char *parent_names[4]; u8 num_parents, num_clocks; int ret, n; @@ -1438,10 +1457,9 @@ static int si5351_i2c_probe(struct i2c_client *client, init.num_parents = 1; } drvdata->xtal.init = &init; - clk = devm_clk_register(&client->dev, &drvdata->xtal); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, &drvdata->xtal); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } @@ -1456,11 +1474,10 @@ static int si5351_i2c_probe(struct i2c_client *client, init.num_parents = 1; } drvdata->clkin.init = &init; - clk = devm_clk_register(&client->dev, &drvdata->clkin); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, &drvdata->clkin); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } } @@ -1480,10 +1497,9 @@ static int si5351_i2c_probe(struct i2c_client *client, init.flags = 0; init.parent_names = parent_names; init.num_parents = num_parents; - clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, &drvdata->pll[0].hw); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } @@ -1505,10 +1521,9 @@ static int si5351_i2c_probe(struct i2c_client *client, init.parent_names = parent_names; init.num_parents = num_parents; } - clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, &drvdata->pll[1].hw); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } @@ -1524,13 +1539,9 @@ static int si5351_i2c_probe(struct i2c_client *client, sizeof(*drvdata->msynth), GFP_KERNEL); drvdata->clkout = devm_kzalloc(&client->dev, num_clocks * sizeof(*drvdata->clkout), GFP_KERNEL); + drvdata->num_clkout = num_clocks; - drvdata->onecell.clk_num = num_clocks; - drvdata->onecell.clks = devm_kzalloc(&client->dev, - num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL); - - if (WARN_ON(!drvdata->msynth || !drvdata->clkout || - !drvdata->onecell.clks)) { + if (WARN_ON(!drvdata->msynth || !drvdata->clkout)) { ret = -ENOMEM; goto err_clk; } @@ -1547,11 +1558,11 @@ static int si5351_i2c_probe(struct i2c_client *client, init.flags |= CLK_SET_RATE_PARENT; init.parent_names = parent_names; init.num_parents = 2; - clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, + &drvdata->msynth[n].hw); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } } @@ -1575,19 +1586,19 @@ static int si5351_i2c_probe(struct i2c_client *client, init.flags |= CLK_SET_RATE_PARENT; init.parent_names = parent_names; init.num_parents = num_parents; - clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw); - if (IS_ERR(clk)) { + ret = devm_clk_hw_register(&client->dev, + &drvdata->clkout[n].hw); + if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - ret = PTR_ERR(clk); goto err_clk; } - drvdata->onecell.clks[n] = clk; /* set initial clkout rate */ if (pdata->clkout[n].rate != 0) { int ret; - ret = clk_set_rate(clk, pdata->clkout[n].rate); + ret = clk_set_rate(drvdata->clkout[n].hw.clk, + pdata->clkout[n].rate); if (ret != 0) { dev_err(&client->dev, "Cannot set rate : %d\n", ret); @@ -1595,8 +1606,8 @@ static int si5351_i2c_probe(struct i2c_client *client, } } - ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, - &drvdata->onecell); + ret = of_clk_add_hw_provider(client->dev.of_node, si53351_of_clk_get, + drvdata); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); goto err_clk; diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c index d566485..646af1d 100644 --- a/drivers/clk/clk-si570.c +++ b/drivers/clk/clk-si570.c @@ -408,7 +408,6 @@ static int si570_probe(struct i2c_client *client, { struct clk_si570 *data; struct clk_init_data init; - struct clk *clk; u32 initial_fout, factory_fout, stability; int err; enum clk_si570_variant variant = id->driver_data; @@ -462,13 +461,13 @@ static int si570_probe(struct i2c_client *client, if (err) return err; - clk = devm_clk_register(&client->dev, &data->hw); - if (IS_ERR(clk)) { + err = devm_clk_hw_register(&client->dev, &data->hw); + if (err) { dev_err(&client->dev, "clock registration failed\n"); - return PTR_ERR(clk); + return err; } - err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get, - clk); + err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get, + &data->hw); if (err) { dev_err(&client->dev, "unable to add clk provider\n"); return err; @@ -477,7 +476,7 @@ static int si570_probe(struct i2c_client *client, /* Read the requested initial output frequency from device tree */ if (!of_property_read_u32(client->dev.of_node, "clock-frequency", &initial_fout)) { - err = clk_set_rate(clk, initial_fout); + err = clk_set_rate(data->hw.clk, initial_fout); if (err) { of_clk_del_provider(client->dev.of_node); return err; diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 697c667..7b222a5 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -26,60 +26,73 @@ #include <linux/mfd/twl6040.h> #include <linux/clk-provider.h> -struct twl6040_clk { +struct twl6040_pdmclk { struct twl6040 *twl6040; struct device *dev; - struct clk_hw mcpdm_fclk; - struct clk *clk; + struct clk_hw pdmclk_hw; int enabled; }; -static int twl6040_bitclk_is_enabled(struct clk_hw *hw) +static int twl6040_pdmclk_is_prepared(struct clk_hw *hw) { - struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk, - mcpdm_fclk); - return twl6040_clk->enabled; + struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, + pdmclk_hw); + + return pdmclk->enabled; } -static int twl6040_bitclk_prepare(struct clk_hw *hw) +static int twl6040_pdmclk_prepare(struct clk_hw *hw) { - struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk, - mcpdm_fclk); + struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, + pdmclk_hw); int ret; - ret = twl6040_power(twl6040_clk->twl6040, 1); + ret = twl6040_power(pdmclk->twl6040, 1); if (!ret) - twl6040_clk->enabled = 1; + pdmclk->enabled = 1; return ret; } -static void twl6040_bitclk_unprepare(struct clk_hw *hw) +static void twl6040_pdmclk_unprepare(struct clk_hw *hw) { - struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk, - mcpdm_fclk); + struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, + pdmclk_hw); int ret; - ret = twl6040_power(twl6040_clk->twl6040, 0); + ret = twl6040_power(pdmclk->twl6040, 0); if (!ret) - twl6040_clk->enabled = 0; + pdmclk->enabled = 0; + } -static const struct clk_ops twl6040_mcpdm_ops = { - .is_enabled = twl6040_bitclk_is_enabled, - .prepare = twl6040_bitclk_prepare, - .unprepare = twl6040_bitclk_unprepare, +static unsigned long twl6040_pdmclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, + pdmclk_hw); + + return twl6040_get_sysclk(pdmclk->twl6040); +} + +static const struct clk_ops twl6040_pdmclk_ops = { + .is_prepared = twl6040_pdmclk_is_prepared, + .prepare = twl6040_pdmclk_prepare, + .unprepare = twl6040_pdmclk_unprepare, + .recalc_rate = twl6040_pdmclk_recalc_rate, }; -static struct clk_init_data wm831x_clkout_init = { - .name = "mcpdm_fclk", - .ops = &twl6040_mcpdm_ops, +static struct clk_init_data twl6040_pdmclk_init = { + .name = "pdmclk", + .ops = &twl6040_pdmclk_ops, + .flags = CLK_GET_RATE_NOCACHE, }; -static int twl6040_clk_probe(struct platform_device *pdev) +static int twl6040_pdmclk_probe(struct platform_device *pdev) { struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent); - struct twl6040_clk *clkdata; + struct twl6040_pdmclk *clkdata; + int ret; clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); if (!clkdata) @@ -88,26 +101,28 @@ static int twl6040_clk_probe(struct platform_device *pdev) clkdata->dev = &pdev->dev; clkdata->twl6040 = twl6040; - clkdata->mcpdm_fclk.init = &wm831x_clkout_init; - clkdata->clk = devm_clk_register(&pdev->dev, &clkdata->mcpdm_fclk); - if (IS_ERR(clkdata->clk)) - return PTR_ERR(clkdata->clk); + clkdata->pdmclk_hw.init = &twl6040_pdmclk_init; + ret = devm_clk_hw_register(&pdev->dev, &clkdata->pdmclk_hw); + if (ret) + return ret; platform_set_drvdata(pdev, clkdata); - return 0; + return of_clk_add_hw_provider(pdev->dev.parent->of_node, + of_clk_hw_simple_get, + &clkdata->pdmclk_hw); } -static struct platform_driver twl6040_clk_driver = { +static struct platform_driver twl6040_pdmclk_driver = { .driver = { - .name = "twl6040-clk", + .name = "twl6040-pdmclk", }, - .probe = twl6040_clk_probe, + .probe = twl6040_pdmclk_probe, }; -module_platform_driver(twl6040_clk_driver); +module_platform_driver(twl6040_pdmclk_driver); MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock"); MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); -MODULE_ALIAS("platform:twl6040-clk"); +MODULE_ALIAS("platform:twl6040-pdmclk"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 37368a3..4161a6f 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -232,7 +232,7 @@ static const struct clk_ops vt8500_gated_divisor_clk_ops = { static __init void vtwm_device_clk_init(struct device_node *node) { u32 en_reg, div_reg; - struct clk *clk; + struct clk_hw *hw; struct clk_device *dev_clk; const char *clk_name = node->name; const char *parent_name; @@ -301,13 +301,14 @@ static __init void vtwm_device_clk_init(struct device_node *node) dev_clk->hw.init = &init; - clk = clk_register(NULL, &dev_clk->hw); - if (WARN_ON(IS_ERR(clk))) { + hw = &dev_clk->hw; + rc = clk_hw_register(NULL, hw); + if (WARN_ON(rc)) { kfree(dev_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); + rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); + clk_hw_register_clkdev(hw, clk_name, NULL); } CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); @@ -681,7 +682,7 @@ static const struct clk_ops vtwm_pll_ops = { static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type) { u32 reg; - struct clk *clk; + struct clk_hw *hw; struct clk_pll *pll_clk; const char *clk_name = node->name; const char *parent_name; @@ -714,13 +715,14 @@ static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type) pll_clk->hw.init = &init; - clk = clk_register(NULL, &pll_clk->hw); - if (WARN_ON(IS_ERR(clk))) { + hw = &pll_clk->hw; + rc = clk_hw_register(NULL, &pll_clk->hw); + if (WARN_ON(rc)) { kfree(pll_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); + rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); + clk_hw_register_clkdev(hw, clk_name, NULL); } diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 88def4b..f4fdac5 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -24,9 +24,6 @@ struct wm831x_clk { struct clk_hw xtal_hw; struct clk_hw fll_hw; struct clk_hw clkout_hw; - struct clk *xtal; - struct clk *fll; - struct clk *clkout; bool xtal_ena; }; @@ -370,19 +367,19 @@ static int wm831x_clk_probe(struct platform_device *pdev) clkdata->xtal_ena = ret & WM831X_XTAL_ENA; clkdata->xtal_hw.init = &wm831x_xtal_init; - clkdata->xtal = devm_clk_register(&pdev->dev, &clkdata->xtal_hw); - if (IS_ERR(clkdata->xtal)) - return PTR_ERR(clkdata->xtal); + ret = devm_clk_hw_register(&pdev->dev, &clkdata->xtal_hw); + if (ret) + return ret; clkdata->fll_hw.init = &wm831x_fll_init; - clkdata->fll = devm_clk_register(&pdev->dev, &clkdata->fll_hw); - if (IS_ERR(clkdata->fll)) - return PTR_ERR(clkdata->fll); + ret = devm_clk_hw_register(&pdev->dev, &clkdata->fll_hw); + if (ret) + return ret; clkdata->clkout_hw.init = &wm831x_clkout_init; - clkdata->clkout = devm_clk_register(&pdev->dev, &clkdata->clkout_hw); - if (IS_ERR(clkdata->clkout)) - return PTR_ERR(clkdata->clkout); + ret = devm_clk_hw_register(&pdev->dev, &clkdata->clkout_hw); + if (ret) + return ret; platform_set_drvdata(pdev, clkdata); diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 3433132..5daddf5 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -217,6 +217,226 @@ static void xgene_pcppllclk_init(struct device_node *np) xgene_pllclk_init(np, PLL_TYPE_PCP); } +/** + * struct xgene_clk_pmd - PMD clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing the fractional scale multiplier (scaler) + * @shift: shift to the unit bit field + * @denom: 1/denominator unit + * @lock: register lock + * Flags: + * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read + * from the register plus one. For example, + * 0 for (0 + 1) / denom, + * 1 for (1 + 1) / denom and etc. + * If this flag is set, it is + * 0 for (denom - 0) / denom, + * 1 for (denom - 1) / denom and etc. + * + */ +struct xgene_clk_pmd { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u32 mask; + u64 denom; + u32 flags; + spinlock_t *lock; +}; + +#define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw) + +#define XGENE_CLK_PMD_SCALE_INVERTED BIT(0) +#define XGENE_CLK_PMD_SHIFT 8 +#define XGENE_CLK_PMD_WIDTH 3 + +static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 ret, scale; + u32 val; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + ret = (u64)parent_rate; + + scale = (val & fd->mask) >> fd->shift; + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale++; + + /* freq = parent_rate * scaler / denom */ + do_div(ret, fd->denom); + ret *= scale; + if (ret == 0) + ret = (u64)parent_rate; + + return ret; +} + +static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + u64 ret, scale; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + /* freq = parent_rate * scaler / denom */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, *parent_rate); + + ret = (u64)*parent_rate * scale; + do_div(ret, fd->denom); + + return ret; +} + +static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 scale, ret; + u32 val; + + /* + * Compute the scaler: + * + * freq = parent_rate * scaler / denom, or + * scaler = freq * denom / parent_rate + */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate); + + /* Check if inverted */ + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale--; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + val &= ~fd->mask; + val |= (scale << fd->shift); + clk_writel(val, fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + return 0; +} + +static const struct clk_ops xgene_clk_pmd_ops = { + .recalc_rate = xgene_clk_pmd_recalc_rate, + .round_rate = xgene_clk_pmd_round_rate, + .set_rate = xgene_clk_pmd_set_rate, +}; + +static struct clk * +xgene_register_clk_pmd(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, + u8 width, u64 denom, u32 clk_flags, spinlock_t *lock) +{ + struct xgene_clk_pmd *fd; + struct clk_init_data init; + struct clk *clk; + + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &xgene_clk_pmd_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + fd->reg = reg; + fd->shift = shift; + fd->mask = (BIT(width) - 1) << shift; + fd->denom = denom; + fd->flags = clk_flags; + fd->lock = lock; + fd->hw.init = &init; + + clk = clk_register(dev, &fd->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(fd); + return NULL; + } + + return clk; +} + +static void xgene_pmdclk_init(struct device_node *np) +{ + const char *clk_name = np->full_name; + void __iomem *csr_reg; + struct resource res; + struct clk *clk; + u64 denom; + u32 flags = 0; + int rc; + + /* Check if the entry is disabled */ + if (!of_device_is_available(np)) + return; + + /* Parse the DTS register for resource */ + rc = of_address_to_resource(np, 0, &res); + if (rc != 0) { + pr_err("no DTS register for %s\n", np->full_name); + return; + } + csr_reg = of_iomap(np, 0); + if (!csr_reg) { + pr_err("Unable to map resource for %s\n", np->full_name); + return; + } + of_property_read_string(np, "clock-output-names", &clk_name); + + denom = BIT(XGENE_CLK_PMD_WIDTH); + flags |= XGENE_CLK_PMD_SCALE_INVERTED; + + clk = xgene_register_clk_pmd(NULL, clk_name, + of_clk_get_parent_name(np, 0), 0, + csr_reg, XGENE_CLK_PMD_SHIFT, + XGENE_CLK_PMD_WIDTH, denom, + flags, &clk_lock); + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + pr_debug("Add %s clock\n", clk_name); + } else { + if (csr_reg) + iounmap(csr_reg); + } +} + /* IP Clock */ struct xgene_dev_parameters { void __iomem *csr_reg; /* CSR for IP clock */ @@ -543,6 +763,7 @@ err: CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); +CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init); CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock", diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 820a939..0fb39fe 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1908,10 +1908,6 @@ int clk_set_phase(struct clk *clk, int degrees) clk_prepare_lock(); - /* bail early if nothing to do */ - if (degrees == clk->core->phase) - goto out; - trace_clk_set_phase(clk->core, degrees); if (clk->core->ops->set_phase) @@ -1922,7 +1918,6 @@ int clk_set_phase(struct clk *clk, int degrees) if (!ret) clk->core->phase = degrees; -out: clk_prepare_unlock(); return ret; @@ -2449,8 +2444,16 @@ static int __clk_core_init(struct clk_core *core) hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { struct clk_core *parent = __clk_init_parent(orphan); - if (parent) - clk_core_reparent(orphan, parent); + /* + * we could call __clk_set_parent, but that would result in a + * redundant call to the .set_rate op, if it exists + */ + if (parent) { + __clk_set_parent_before(orphan, parent); + __clk_set_parent_after(orphan, parent, NULL); + __clk_recalc_accuracies(orphan); + __clk_recalc_rates(orphan, 0); + } } /* @@ -2491,7 +2494,7 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id, /* This is to allow this function to be chained to others */ if (IS_ERR_OR_NULL(hw)) - return (struct clk *) hw; + return ERR_CAST(hw); clk = kzalloc(sizeof(*clk), GFP_KERNEL); if (!clk) @@ -3166,19 +3169,14 @@ __of_clk_get_hw_from_provider(struct of_clk_provider *provider, struct of_phandle_args *clkspec) { struct clk *clk; - struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); - if (provider->get_hw) { - hw = provider->get_hw(clkspec, provider->data); - } else if (provider->get) { - clk = provider->get(clkspec, provider->data); - if (!IS_ERR(clk)) - hw = __clk_get_hw(clk); - else - hw = ERR_CAST(clk); - } + if (provider->get_hw) + return provider->get_hw(clkspec, provider->data); - return hw; + clk = provider->get(clkspec, provider->data); + if (IS_ERR(clk)) + return ERR_CAST(clk); + return __clk_get_hw(clk); } struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, @@ -3186,7 +3184,7 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, { struct of_clk_provider *provider; struct clk *clk = ERR_PTR(-EPROBE_DEFER); - struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); + struct clk_hw *hw; if (!clkspec) return ERR_PTR(-EINVAL); @@ -3194,12 +3192,13 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, /* Check if we have such a provider in our array */ mutex_lock(&of_clk_mutex); list_for_each_entry(provider, &of_clk_providers, link) { - if (provider->node == clkspec->np) + if (provider->node == clkspec->np) { hw = __of_clk_get_hw_from_provider(provider, clkspec); - if (!IS_ERR(hw)) { clk = __clk_create_clk(hw, dev_id, con_id); + } - if (!IS_ERR(clk) && !__clk_get(clk)) { + if (!IS_ERR(clk)) { + if (!__clk_get(clk)) { __clk_free_clk(clk); clk = ERR_PTR(-ENOENT); } @@ -3451,6 +3450,10 @@ void __init of_clk_init(const struct of_device_id *matches) &clk_provider_list, node) { if (force || parent_ready(clk_provider->np)) { + /* Don't populate platform devices */ + of_node_set_flag(clk_provider->np, + OF_POPULATED); + clk_provider->clk_init_cb(clk_provider->np); of_clk_set_defaults(clk_provider->np, true); diff --git a/drivers/clk/h8300/clk-div.c b/drivers/clk/h8300/clk-div.c index 4bf44a2..715b882 100644 --- a/drivers/clk/h8300/clk-div.c +++ b/drivers/clk/h8300/clk-div.c @@ -14,7 +14,7 @@ static DEFINE_SPINLOCK(clklock); static void __init h8300_div_clk_setup(struct device_node *node) { unsigned int num_parents; - struct clk *clk; + struct clk_hw *hw; const char *clk_name = node->name; const char *parent_name; void __iomem *divcr = NULL; @@ -38,15 +38,15 @@ static void __init h8300_div_clk_setup(struct device_node *node) parent_name = of_clk_get_parent_name(node, 0); of_property_read_u32(node, "renesas,width", &width); - clk = clk_register_divider(NULL, clk_name, parent_name, + hw = clk_hw_register_divider(NULL, clk_name, parent_name, CLK_SET_RATE_GATE, divcr, offset, width, CLK_DIVIDER_POWER_OF_TWO, &clklock); - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (!IS_ERR(hw)) { + of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); return; } pr_err("%s: failed to register %s div clock (%ld)\n", - __func__, clk_name, PTR_ERR(clk)); + __func__, clk_name, PTR_ERR(hw)); error: if (divcr) iounmap(divcr); diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c index c9c2fd5..a263124 100644 --- a/drivers/clk/h8300/clk-h8s2678.c +++ b/drivers/clk/h8300/clk-h8s2678.c @@ -84,11 +84,11 @@ static const struct clk_ops pll_ops = { static void __init h8s2678_pll_clk_setup(struct device_node *node) { unsigned int num_parents; - struct clk *clk; const char *clk_name = node->name; const char *parent_name; struct pll_clock *pll_clock; struct clk_init_data init; + int ret; num_parents = of_clk_get_parent_count(node); if (!num_parents) { @@ -121,14 +121,14 @@ static void __init h8s2678_pll_clk_setup(struct device_node *node) init.num_parents = 1; pll_clock->hw.init = &init; - clk = clk_register(NULL, &pll_clock->hw); - if (IS_ERR(clk)) { - pr_err("%s: failed to register %s div clock (%ld)\n", - __func__, clk_name, PTR_ERR(clk)); + ret = clk_hw_register(NULL, &pll_clock->hw); + if (ret) { + pr_err("%s: failed to register %s div clock (%d)\n", + __func__, clk_name, ret); goto unmap_pllcr; } - of_clk_add_provider(node, of_clk_src_simple_get, clk); + of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clock->hw); return; unmap_pllcr: diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index b0978d3..203cad6 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -66,20 +66,22 @@ static const char *std_sel[] = {"ppll", "arm"}; static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"}; enum mx35_clks { - ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg, - arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, esdhc_sel, - esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, spdif_div_pre, - spdif_div_post, ssi_sel, ssi1_div_pre, ssi1_div_post, ssi2_div_pre, - ssi2_div_post, usb_sel, usb_div, nfc_div, asrc_gate, pata_gate, - audmux_gate, can1_gate, can2_gate, cspi1_gate, cspi2_gate, ect_gate, - edio_gate, emi_gate, epit1_gate, epit2_gate, esai_gate, esdhc1_gate, - esdhc2_gate, esdhc3_gate, fec_gate, gpio1_gate, gpio2_gate, gpio3_gate, - gpt_gate, i2c1_gate, i2c2_gate, i2c3_gate, iomuxc_gate, ipu_gate, - kpp_gate, mlb_gate, mshc_gate, owire_gate, pwm_gate, rngc_gate, - rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate, - ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate, - wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate, - gpu2d_gate, ckil, clk_max + /* 0 */ ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, + /* 9 */ ipg, arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, + /* 15 */ esdhc_sel, esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, + /* 20 */ spdif_div_pre, spdif_div_post, ssi_sel, ssi1_div_pre, + /* 24 */ ssi1_div_post, ssi2_div_pre, ssi2_div_post, usb_sel, usb_div, + /* 29 */ nfc_div, asrc_gate, pata_gate, audmux_gate, can1_gate, + /* 34 */ can2_gate, cspi1_gate, cspi2_gate, ect_gate, edio_gate, + /* 39 */ emi_gate, epit1_gate, epit2_gate, esai_gate, esdhc1_gate, + /* 44 */ esdhc2_gate, esdhc3_gate, fec_gate, gpio1_gate, gpio2_gate, + /* 49 */ gpio3_gate, gpt_gate, i2c1_gate, i2c2_gate, i2c3_gate, + /* 54 */ iomuxc_gate, ipu_gate, kpp_gate, mlb_gate, mshc_gate, + /* 59 */ owire_gate, pwm_gate, rngc_gate, rtc_gate, rtic_gate, scc_gate, + /* 65 */ sdma_gate, spba_gate, spdif_gate, ssi1_gate, ssi2_gate, + /* 70 */ uart1_gate, uart2_gate, uart3_gate, usbotg_gate, wdog_gate, + /* 75 */ max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate, + /* 81 */ gpu2d_gate, ckil, clk_max }; static struct clk *clk[clk_max]; @@ -115,7 +117,7 @@ static void __init _mx35_clocks_init(void) } clk[ckih] = imx_clk_fixed("ckih", 24000000); - clk[ckil] = imx_clk_fixed("ckih", 32768); + clk[ckil] = imx_clk_fixed("ckil", 32768); clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL); clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL); diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index 29d4c44..1e3c9ea 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -126,6 +126,7 @@ static const char *spdif0_com_sel[] = { "spdif0_podf", "ssi1_root_gate", }; static const char *mx51_spdif1_com_sel[] = { "spdif1_podf", "ssi2_root_gate", }; static const char *step_sels[] = { "lp_apm", }; static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" }; +static const char *ieee1588_sels[] = { "pll3_sw", "pll4_sw", "dummy" /* usbphy2_clk */, "dummy" /* fec_phy_clk */ }; static struct clk *clk[IMX5_CLK_END]; static struct clk_onecell_data clk_data; @@ -543,6 +544,25 @@ static void __init mx53_clocks_init(struct device_node *np) clk[IMX5_CLK_I2C3_GATE] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22); clk[IMX5_CLK_SATA_GATE] = imx_clk_gate2("sata_gate", "ipg", MXC_CCM_CCGR4, 2); + clk[IMX5_CLK_FIRI_SEL] = imx_clk_mux("firi_sel", MXC_CCM_CSCMR2, 12, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_FIRI_PRED] = imx_clk_divider("firi_pred", "firi_sel", MXC_CCM_CSCDR3, 6, 3); + clk[IMX5_CLK_FIRI_PODF] = imx_clk_divider("firi_podf", "firi_pred", MXC_CCM_CSCDR3, 0, 6); + clk[IMX5_CLK_FIRI_SERIAL_GATE] = imx_clk_gate2("firi_serial_gate", "firi_podf", MXC_CCM_CCGR1, 28); + clk[IMX5_CLK_FIRI_IPG_GATE] = imx_clk_gate2("firi_ipg_gate", "ipg", MXC_CCM_CCGR1, 26); + + clk[IMX5_CLK_CSI0_MCLK1_SEL] = imx_clk_mux("csi0_mclk1_sel", MXC_CCM_CSCMR2, 22, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_CSI0_MCLK1_PRED] = imx_clk_divider("csi0_mclk1_pred", "csi0_mclk1_sel", MXC_CCM_CSCDR4, 6, 3); + clk[IMX5_CLK_CSI0_MCLK1_PODF] = imx_clk_divider("csi0_mclk1_podf", "csi0_mclk1_pred", MXC_CCM_CSCDR4, 0, 6); + clk[IMX5_CLK_CSI0_MCLK1_GATE] = imx_clk_gate2("csi0_mclk1_serial_gate", "csi0_mclk1_podf", MXC_CCM_CCGR6, 4); + + clk[IMX5_CLK_IEEE1588_SEL] = imx_clk_mux("ieee1588_sel", MXC_CCM_CSCMR2, 14, 2, + ieee1588_sels, ARRAY_SIZE(ieee1588_sels)); + clk[IMX5_CLK_IEEE1588_PRED] = imx_clk_divider("ieee1588_pred", "ieee1588_sel", MXC_CCM_CSCDR2, 6, 3); + clk[IMX5_CLK_IEEE1588_PODF] = imx_clk_divider("ieee1588_podf", "ieee1588_pred", MXC_CCM_CSCDR2, 0, 6); + clk[IMX5_CLK_IEEE1588_GATE] = imx_clk_gate2("ieee1588_serial_gate", "ieee1588_podf", MXC_CCM_CCGR7, 6); + clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4, mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel)); clk[IMX5_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", MXC_CCM_CCOSR, 4, 3); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index ba1c1ae..ce8ea10 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -318,11 +318,16 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_IPG_PER_SEL] = imx_clk_mux("ipg_per_sel", base + 0x1c, 6, 1, ipg_per_sels, ARRAY_SIZE(ipg_per_sels)); clk[IMX6QDL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels_2, ARRAY_SIZE(gpu2d_core_sels_2)); + } else if (clk_on_imx6dl()) { + clk[IMX6QDL_CLK_MLB_SEL] = imx_clk_mux("mlb_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); } else { clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); } clk[IMX6QDL_CLK_GPU3D_CORE_SEL] = imx_clk_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels)); - clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); + if (clk_on_imx6dl()) + clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); + else + clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); @@ -400,9 +405,15 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); clk[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); } - clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); + if (clk_on_imx6dl()) + clk[IMX6QDL_CLK_MLB_PODF] = imx_clk_divider("mlb_podf", "mlb_sel", base + 0x18, 23, 3); + else + clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); clk[IMX6QDL_CLK_GPU3D_CORE_PODF] = imx_clk_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3); - clk[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); + if (clk_on_imx6dl()) + clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 29, 3); + else + clk[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); clk[IMX6QDL_CLK_IPU1_PODF] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); clk[IMX6QDL_CLK_IPU2_PODF] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); clk[IMX6QDL_CLK_LDB_DI0_PODF] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); @@ -473,14 +484,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai); clk[IMX6QDL_CLK_GPT_IPG] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); clk[IMX6QDL_CLK_GPT_IPG_PER] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); - if (clk_on_imx6dl()) - /* - * The multiplexer and divider of imx6q clock gpu3d_shader get - * redefined/reused as gpu2d_core_sel and gpu2d_core_podf on imx6dl. - */ - clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24); - else - clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); + clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); clk[IMX6QDL_CLK_GPU3D_CORE] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); clk[IMX6QDL_CLK_HDMI_IAHB] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); clk[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_gate2("hdmi_isfr", "video_27m", base + 0x70, 4); @@ -511,7 +515,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * The multiplexer and divider of the imx6q clock gpu2d get * redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl. */ - clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); + clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "mlb_podf", base + 0x74, 18); else clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); @@ -629,6 +633,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) if (IS_ENABLED(CONFIG_PCI_IMX6)) clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]); + /* + * Initialize the GPU clock muxes, so that the maximum specified clock + * rates for the respective SoC are not exceeded. + */ + if (clk_on_imx6dl()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + } else if (clk_on_imx6q()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_MMDC_CH0_AXI]); + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL3_USB_OTG]); + } + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 6ed4f8f..e7c7353 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -22,43 +22,63 @@ #include "clk.h" +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; + +static struct clk_div_table test_div_table[] = { + { .val = 3, .div = 1, }, + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static struct clk_div_table post_div_table[] = { + { .val = 3, .div = 4, }, + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 1, }, + { } +}; + static struct clk *clks[IMX7D_CLK_END]; static const char *arm_a7_sel[] = { "osc", "pll_arm_main_clk", "pll_enet_500m_clk", "pll_dram_main_clk", - "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_audio_main_clk", + "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_audio_post_div", "pll_usb_main_clk", }; static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_enet_250m_clk", "pll_sys_pfd2_270m_clk", - "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; static const char *arm_m0_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_enet_125m_clk", "pll_sys_pfd2_135m_clk", - "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_sys_pfd7_clk", }; + "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd7_clk", }; static const char *disp_axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd6_clk", - "pll_sys_pfd7_clk", "pll_audio_main_clk", "pll_video_main_clk", }; + "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_main_clk", }; static const char *enet_axi_sel[] = { "osc", "pll_sys_pfd2_270m_clk", "pll_dram_533m_clk", "pll_enet_250m_clk", - "pll_sys_main_240m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_sys_main_240m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd4_clk", }; static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk", "pll_dram_533m_clk", "pll_sys_main_240m_clk", "pll_sys_pfd2_135m_clk", "pll_sys_pfd6_clk", "pll_enet_250m_clk", - "pll_audio_main_clk", }; + "pll_audio_post_div", }; static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_270m_clk", "pll_dram_533m_clk", "pll_sys_pfd0_392m_clk", - "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_main_clk", + "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_post_div", "pll_video_main_clk", }; static const char *dram_phym_sel[] = { "pll_dram_main_clk", @@ -69,13 +89,13 @@ static const char *dram_sel[] = { "pll_dram_main_clk", static const char *dram_phym_alt_sel[] = { "osc", "pll_dram_533m_clk", "pll_sys_main_clk", "pll_enet_500m_clk", - "pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_main_clk", + "pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_main_clk", }; static const char *dram_alt_sel[] = { "osc", "pll_dram_533m_clk", "pll_sys_main_clk", "pll_enet_500m_clk", "pll_enet_250m_clk", "pll_sys_pfd0_392m_clk", - "pll_audio_main_clk", "pll_sys_pfd2_270m_clk", }; + "pll_audio_post_div", "pll_sys_pfd2_270m_clk", }; static const char *usb_hsic_sel[] = { "osc", "pll_sys_main_clk", "pll_usb_main_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk", @@ -101,53 +121,53 @@ static const char *lcdif_pixel_sel[] = { "osc", "pll_sys_pfd5_clk", static const char *mipi_dsi_sel[] = { "osc", "pll_sys_pfd5_clk", "pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk", - "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_main_clk", }; + "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", }; static const char *mipi_csi_sel[] = { "osc", "pll_sys_pfd4_clk", "pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk", - "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_main_clk", }; + "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", }; static const char *mipi_dphy_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_dram_533m_clk", "pll_sys_pfd5_clk", "ref_1m_clk", "ext_clk_2", "pll_video_main_clk", "ext_clk_3", }; static const char *sai1_sel[] = { "osc", "pll_sys_pfd2_135m_clk", - "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk", "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", }; static const char *sai2_sel[] = { "osc", "pll_sys_pfd2_135m_clk", - "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk", "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", }; static const char *sai3_sel[] = { "osc", "pll_sys_pfd2_135m_clk", - "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk", "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_3", }; static const char *spdif_sel[] = { "osc", "pll_sys_pfd2_135m_clk", - "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk", "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_3_clk", }; static const char *enet1_ref_sel[] = { "osc", "pll_enet_125m_clk", "pll_enet_50m_clk", "pll_enet_25m_clk", - "pll_sys_main_120m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk", "ext_clk_4", }; static const char *enet1_time_sel[] = { "osc", "pll_enet_100m_clk", - "pll_audio_main_clk", "ext_clk_1", "ext_clk_2", "ext_clk_3", + "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3", "ext_clk_4", "pll_video_main_clk", }; static const char *enet2_ref_sel[] = { "osc", "pll_enet_125m_clk", "pll_enet_50m_clk", "pll_enet_25m_clk", - "pll_sys_main_120m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk", "ext_clk_4", }; static const char *enet2_time_sel[] = { "osc", "pll_enet_100m_clk", - "pll_audio_main_clk", "ext_clk_1", "ext_clk_2", "ext_clk_3", + "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3", "ext_clk_4", "pll_video_main_clk", }; static const char *enet_phy_ref_sel[] = { "osc", "pll_enet_25m_clk", "pll_enet_50m_clk", "pll_enet_125m_clk", - "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd3_clk", }; static const char *eim_sel[] = { "osc", "pll_sys_pfd2_135m_clk", @@ -188,22 +208,22 @@ static const char *can2_sel[] = { "osc", "pll_sys_main_120m_clk", static const char *i2c1_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_enet_50m_clk", "pll_dram_533m_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk", + "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", "pll_sys_pfd2_135m_clk", }; static const char *i2c2_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_enet_50m_clk", "pll_dram_533m_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk", + "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", "pll_sys_pfd2_135m_clk", }; static const char *i2c3_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_enet_50m_clk", "pll_dram_533m_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk", + "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", "pll_sys_pfd2_135m_clk", }; static const char *i2c4_sel[] = { "osc", "pll_sys_main_120m_clk", "pll_enet_50m_clk", "pll_dram_533m_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk", + "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", "pll_sys_pfd2_135m_clk", }; static const char *uart1_sel[] = { "osc", "pll_sys_main_240m_clk", @@ -262,32 +282,32 @@ static const char *ecspi4_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_usb_main_clk", }; static const char *pwm1_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_1", "ref_1m_clk", "pll_video_main_clk", }; static const char *pwm2_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_1", "ref_1m_clk", "pll_video_main_clk", }; static const char *pwm3_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_2", "ref_1m_clk", "pll_video_main_clk", }; static const char *pwm4_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_2", "ref_1m_clk", "pll_video_main_clk", }; static const char *flextimer1_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_3", "ref_1m_clk", "pll_video_main_clk", }; static const char *flextimer2_sel[] = { "osc", "pll_enet_100m_clk", - "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", "ext_clk_3", "ref_1m_clk", "pll_video_main_clk", }; static const char *sim1_sel[] = { "osc", "pll_sys_pfd2_135m_clk", "pll_sys_main_120m_clk", "pll_dram_533m_clk", - "pll_usb_main_clk", "pll_audio_main_clk", "pll_enet_125m_clk", + "pll_usb_main_clk", "pll_audio_post_div", "pll_enet_125m_clk", "pll_sys_pfd7_clk", }; static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk", @@ -297,19 +317,19 @@ static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk", static const char *gpt1_sel[] = { "osc", "pll_enet_100m_clk", "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk", - "ref_1m_clk", "pll_audio_main_clk", "ext_clk_1", }; + "ref_1m_clk", "pll_audio_post_div", "ext_clk_1", }; static const char *gpt2_sel[] = { "osc", "pll_enet_100m_clk", "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk", - "ref_1m_clk", "pll_audio_main_clk", "ext_clk_2", }; + "ref_1m_clk", "pll_audio_post_div", "ext_clk_2", }; static const char *gpt3_sel[] = { "osc", "pll_enet_100m_clk", "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk", - "ref_1m_clk", "pll_audio_main_clk", "ext_clk_3", }; + "ref_1m_clk", "pll_audio_post_div", "ext_clk_3", }; static const char *gpt4_sel[] = { "osc", "pll_enet_100m_clk", "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk", - "ref_1m_clk", "pll_audio_main_clk", "ext_clk_4", }; + "ref_1m_clk", "pll_audio_post_div", "ext_clk_4", }; static const char *trace_sel[] = { "osc", "pll_sys_pfd2_135m_clk", "pll_sys_main_120m_clk", "pll_dram_533m_clk", @@ -323,12 +343,12 @@ static const char *wdog_sel[] = { "osc", "pll_sys_pfd2_135m_clk", static const char *csi_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk", "pll_sys_main_120m_clk", "pll_dram_533m_clk", - "pll_enet_125m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; static const char *audio_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk", "pll_sys_main_120m_clk", "pll_dram_533m_clk", - "pll_enet_125m_clk", "pll_audio_main_clk", "pll_video_main_clk", + "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; static const char *wrclk_sel[] = { "osc", "pll_enet_40m_clk", @@ -342,13 +362,13 @@ static const char *clko1_sel[] = { "osc", "pll_sys_main_clk", static const char *clko2_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_166m_clk", "pll_sys_pfd4_clk", - "pll_audio_main_clk", "pll_video_main_clk", "ckil", }; + "pll_audio_post_div", "pll_video_main_clk", "ckil", }; static const char *lvds1_sel[] = { "pll_arm_main_clk", "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_332m_clk", "pll_sys_pfd2_270m_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk", "pll_sys_pfd5_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", - "pll_audio_main_clk", "pll_video_main_clk", "pll_enet_500m_clk", + "pll_audio_post_div", "pll_video_main_clk", "pll_enet_500m_clk", "pll_enet_250m_clk", "pll_enet_125m_clk", "pll_enet_100m_clk", "pll_enet_50m_clk", "pll_enet_40m_clk", "pll_enet_25m_clk", "pll_dram_main_clk", }; @@ -430,6 +450,11 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_PLL_AUDIO_MAIN_CLK] = imx_clk_gate("pll_audio_main_clk", "pll_audio_main_bypass", base + 0xf0, 13); clks[IMX7D_PLL_VIDEO_MAIN_CLK] = imx_clk_gate("pll_video_main_clk", "pll_video_main_bypass", base + 0x130, 13); + clks[IMX7D_PLL_AUDIO_TEST_DIV] = clk_register_divider_table(NULL, "pll_audio_test_div", "pll_audio_main_clk", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 19, 2, 0, test_div_table, &imx_ccm_lock); + clks[IMX7D_PLL_AUDIO_POST_DIV] = clk_register_divider_table(NULL, "pll_audio_post_div", "pll_audio_test_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 22, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX7D_PLL_SYS_PFD0_392M_CLK] = imx_clk_pfd("pll_sys_pfd0_392m_clk", "pll_sys_main_clk", base + 0xc0, 0); clks[IMX7D_PLL_SYS_PFD1_332M_CLK] = imx_clk_pfd("pll_sys_pfd1_332m_clk", "pll_sys_main_clk", base + 0xc0, 1); clks[IMX7D_PLL_SYS_PFD2_270M_CLK] = imx_clk_pfd("pll_sys_pfd2_270m_clk", "pll_sys_main_clk", base + 0xc0, 2); @@ -779,6 +804,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0); clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0); clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0); + clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0); clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0); clks[IMX7D_PCIE_PHY_ROOT_CLK] = imx_clk_gate4("pcie_phy_root_clk", "pcie_phy_post_div", base + 0x4600, 0); clks[IMX7D_EPDC_PIXEL_ROOT_CLK] = imx_clk_gate4("epdc_pixel_root_clk", "epdc_pixel_post_div", base + 0x44a0, 0); @@ -786,9 +812,12 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_MIPI_DSI_ROOT_CLK] = imx_clk_gate4("mipi_dsi_root_clk", "mipi_dsi_post_div", base + 0x4650, 0); clks[IMX7D_MIPI_CSI_ROOT_CLK] = imx_clk_gate4("mipi_csi_root_clk", "mipi_csi_post_div", base + 0x4640, 0); clks[IMX7D_MIPI_DPHY_ROOT_CLK] = imx_clk_gate4("mipi_dphy_root_clk", "mipi_dphy_post_div", base + 0x4660, 0); - clks[IMX7D_SAI1_ROOT_CLK] = imx_clk_gate4("sai1_root_clk", "sai1_post_div", base + 0x48c0, 0); - clks[IMX7D_SAI2_ROOT_CLK] = imx_clk_gate4("sai2_root_clk", "sai2_post_div", base + 0x48d0, 0); - clks[IMX7D_SAI3_ROOT_CLK] = imx_clk_gate4("sai3_root_clk", "sai3_post_div", base + 0x48e0, 0); + clks[IMX7D_SAI1_ROOT_CLK] = imx_clk_gate2_shared2("sai1_root_clk", "sai1_post_div", base + 0x48c0, 0, &share_count_sai1); + clks[IMX7D_SAI1_IPG_CLK] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_root_clk", base + 0x48c0, 0, &share_count_sai1); + clks[IMX7D_SAI2_ROOT_CLK] = imx_clk_gate2_shared2("sai2_root_clk", "sai2_post_div", base + 0x48d0, 0, &share_count_sai2); + clks[IMX7D_SAI2_IPG_CLK] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_root_clk", base + 0x48d0, 0, &share_count_sai2); + clks[IMX7D_SAI3_ROOT_CLK] = imx_clk_gate2_shared2("sai3_root_clk", "sai3_post_div", base + 0x48e0, 0, &share_count_sai3); + clks[IMX7D_SAI3_IPG_CLK] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_root_clk", base + 0x48e0, 0, &share_count_sai3); clks[IMX7D_SPDIF_ROOT_CLK] = imx_clk_gate4("spdif_root_clk", "spdif_post_div", base + 0x44d0, 0); clks[IMX7D_ENET1_REF_ROOT_CLK] = imx_clk_gate4("enet1_ref_root_clk", "enet1_ref_post_div", base + 0x44e0, 0); clks[IMX7D_ENET1_TIME_ROOT_CLK] = imx_clk_gate4("enet1_time_root_clk", "enet1_time_post_div", base + 0x44f0, 0); @@ -860,8 +889,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) /* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */ clk_set_parent(clks[IMX7D_GPT1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]); - clk_set_parent(clks[IMX7D_ENET_AXI_ROOT_SRC], clks[IMX7D_PLL_ENET_MAIN_250M_CLK]); - /* set uart module clock's parent clock source that must be great then 80MHz */ clk_set_parent(clks[IMX7D_UART1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index a81c038..3799ff8 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -134,6 +134,15 @@ static inline struct clk *imx_clk_gate2_shared(const char *name, shift, 0x3, 0, &imx_ccm_lock, share_count); } +static inline struct clk *imx_clk_gate2_shared2(const char *name, + const char *parent, void __iomem *reg, u8 shift, + unsigned int *share_count) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | + CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, + &imx_ccm_lock, share_count); +} + static inline struct clk *imx_clk_gate2_cgr(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 cgr_val) { diff --git a/drivers/clk/loongson1/Makefile b/drivers/clk/loongson1/Makefile new file mode 100644 index 0000000..b7f6a16 --- /dev/null +++ b/drivers/clk/loongson1/Makefile @@ -0,0 +1,3 @@ +obj-y += clk.o +obj-$(CONFIG_LOONGSON1_LS1B) += clk-loongson1b.o +obj-$(CONFIG_LOONGSON1_LS1C) += clk-loongson1c.o diff --git a/drivers/clk/loongson1/clk-loongson1b.c b/drivers/clk/loongson1/clk-loongson1b.c new file mode 100644 index 0000000..f36a97e --- /dev/null +++ b/drivers/clk/loongson1/clk-loongson1b.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com> + * + * 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 <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/err.h> + +#include <loongson1.h> +#include "clk.h" + +#define OSC (33 * 1000000) +#define DIV_APB 2 + +static DEFINE_SPINLOCK(_lock); + +static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 pll, rate; + + pll = __raw_readl(LS1X_CLK_PLL_FREQ); + rate = 12 + (pll & GENMASK(5, 0)); + rate *= OSC; + rate >>= 1; + + return rate; +} + +static const struct clk_ops ls1x_pll_clk_ops = { + .recalc_rate = ls1x_pll_recalc_rate, +}; + +static const char *const cpu_parents[] = { "cpu_clk_div", "osc_clk", }; +static const char *const ahb_parents[] = { "ahb_clk_div", "osc_clk", }; +static const char *const dc_parents[] = { "dc_clk_div", "osc_clk", }; + +void __init ls1x_clk_init(void) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC); + clk_hw_register_clkdev(hw, "osc_clk", NULL); + + /* clock derived from 33 MHz OSC clk */ + hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk", + &ls1x_pll_clk_ops, 0); + clk_hw_register_clkdev(hw, "pll_clk", NULL); + + /* clock derived from PLL clk */ + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ CPU CLK + * \___ PLL ___ CPU DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk", + CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV, + DIV_CPU_SHIFT, DIV_CPU_WIDTH, + CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ROUND_CLOSEST, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "cpu_clk", cpu_parents, + ARRAY_SIZE(cpu_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk", NULL); + + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ DC CLK + * \___ PLL ___ DC DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, + DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_hw_register_clkdev(hw, "dc_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "dc_clk", dc_parents, + ARRAY_SIZE(dc_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "dc_clk", NULL); + + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ DDR CLK + * \___ PLL ___ DDR DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "ahb_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT, + DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, + &_lock); + clk_hw_register_clkdev(hw, "ahb_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "ahb_clk", ahb_parents, + ARRAY_SIZE(ahb_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "ahb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-dma", NULL); + clk_hw_register_clkdev(hw, "stmmaceth", NULL); + + /* clock derived from AHB clk */ + /* APB clk is always half of the AHB clk */ + hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, + DIV_APB); + clk_hw_register_clkdev(hw, "apb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-ac97", NULL); + clk_hw_register_clkdev(hw, "ls1x-i2c", NULL); + clk_hw_register_clkdev(hw, "ls1x-nand", NULL); + clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL); + clk_hw_register_clkdev(hw, "ls1x-spi", NULL); + clk_hw_register_clkdev(hw, "ls1x-wdt", NULL); + clk_hw_register_clkdev(hw, "serial8250", NULL); +} diff --git a/drivers/clk/loongson1/clk-loongson1c.c b/drivers/clk/loongson1/clk-loongson1c.c new file mode 100644 index 0000000..3466f73 --- /dev/null +++ b/drivers/clk/loongson1/clk-loongson1c.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com> + * + * 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 <linux/clkdev.h> +#include <linux/clk-provider.h> + +#include <loongson1.h> +#include "clk.h" + +#define OSC (24 * 1000000) +#define DIV_APB 1 + +static DEFINE_SPINLOCK(_lock); + +static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 pll, rate; + + pll = __raw_readl(LS1X_CLK_PLL_FREQ); + rate = ((pll >> 8) & 0xff) + ((pll >> 16) & 0xff); + rate *= OSC; + rate >>= 2; + + return rate; +} + +static const struct clk_ops ls1x_pll_clk_ops = { + .recalc_rate = ls1x_pll_recalc_rate, +}; + +static const struct clk_div_table ahb_div_table[] = { + [0] = { .val = 0, .div = 2 }, + [1] = { .val = 1, .div = 4 }, + [2] = { .val = 2, .div = 3 }, + [3] = { .val = 3, .div = 3 }, +}; + +void __init ls1x_clk_init(void) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC); + clk_hw_register_clkdev(hw, "osc_clk", NULL); + + /* clock derived from 24 MHz OSC clk */ + hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk", + &ls1x_pll_clk_ops, 0); + clk_hw_register_clkdev(hw, "pll_clk", NULL); + + hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk", + CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV, + DIV_CPU_SHIFT, DIV_CPU_WIDTH, + CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ROUND_CLOSEST, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "cpu_clk", "cpu_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "cpu_clk", NULL); + + hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, + DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_hw_register_clkdev(hw, "dc_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "dc_clk", "dc_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "dc_clk", NULL); + + hw = clk_hw_register_divider_table(NULL, "ahb_clk_div", "cpu_clk_div", + 0, LS1X_CLK_PLL_FREQ, DIV_DDR_SHIFT, + DIV_DDR_WIDTH, CLK_DIVIDER_ALLOW_ZERO, + ahb_div_table, &_lock); + clk_hw_register_clkdev(hw, "ahb_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "ahb_clk", "ahb_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "ahb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-dma", NULL); + clk_hw_register_clkdev(hw, "stmmaceth", NULL); + + /* clock derived from AHB clk */ + hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, + DIV_APB); + clk_hw_register_clkdev(hw, "apb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-ac97", NULL); + clk_hw_register_clkdev(hw, "ls1x-i2c", NULL); + clk_hw_register_clkdev(hw, "ls1x-nand", NULL); + clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL); + clk_hw_register_clkdev(hw, "ls1x-spi", NULL); + clk_hw_register_clkdev(hw, "ls1x-wdt", NULL); + clk_hw_register_clkdev(hw, "serial8250", NULL); +} diff --git a/drivers/clk/loongson1/clk.c b/drivers/clk/loongson1/clk.c new file mode 100644 index 0000000..cfcfd14 --- /dev/null +++ b/drivers/clk/loongson1/clk.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com> + * + * 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 <linux/clk-provider.h> +#include <linux/slab.h> + +struct clk_hw *__init clk_hw_register_pll(struct device *dev, + const char *name, + const char *parent_name, + const struct clk_ops *ops, + unsigned long flags) +{ + int ret; + struct clk_hw *hw; + struct clk_init_data init; + + /* allocate the divider */ + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + hw->init = &init; + + /* register the clock */ + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(hw); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/loongson1/clk.h b/drivers/clk/loongson1/clk.h new file mode 100644 index 0000000..085d74b --- /dev/null +++ b/drivers/clk/loongson1/clk.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com> + * + * 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. + */ + +#ifndef __LOONGSON1_CLK_H +#define __LOONGSON1_CLK_H + +struct clk_hw *clk_hw_register_pll(struct device *dev, + const char *name, + const char *parent_name, + const struct clk_ops *ops, + unsigned long flags); + +#endif /* __LOONGSON1_CLK_H */ diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig new file mode 100644 index 0000000..380c372 --- /dev/null +++ b/drivers/clk/mediatek/Kconfig @@ -0,0 +1,21 @@ +# +# MediaTek SoC drivers +# +config COMMON_CLK_MEDIATEK + bool + ---help--- + Mediatek SoCs' clock support. + +config COMMON_CLK_MT8135 + bool "Clock driver for Mediatek MT8135" + select COMMON_CLK_MEDIATEK + default ARCH_MEDIATEK + ---help--- + This driver supports Mediatek MT8135 clocks. + +config COMMON_CLK_MT8173 + bool "Clock driver for Mediatek MT8173" + select COMMON_CLK_MEDIATEK + default ARCH_MEDIATEK + ---help--- + This driver supports Mediatek MT8173 clocks. diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 95fdfac..32e7222 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,4 +1,4 @@ -obj-y += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o +obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o -obj-y += clk-mt8135.o -obj-y += clk-mt8173.o +obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o +obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c index 2a76901..d8787bf 100644 --- a/drivers/clk/mediatek/clk-gate.c +++ b/drivers/clk/mediatek/clk-gate.c @@ -97,7 +97,7 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { .disable = mtk_cg_disable_inv, }; -struct clk * __init mtk_clk_register_gate( +struct clk *mtk_clk_register_gate( const char *name, const char *parent_name, struct regmap *regmap, diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c index 10c9860..0ac3aee 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -1074,8 +1074,10 @@ static void __init mtk_apmixedsys_init(struct device_node *node) } mt8173_pll_clk_data = clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); - if (!clk_data) + if (!clk_data) { + iounmap(base); return; + } mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 5ada644..bb30f70 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -24,7 +24,7 @@ #include "clk-mtk.h" #include "clk-gate.h" -struct clk_onecell_data * __init mtk_alloc_clk_data(unsigned int clk_num) +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) { int i; struct clk_onecell_data *clk_data; @@ -49,7 +49,7 @@ err_out: return NULL; } -void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, +void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, int num, struct clk_onecell_data *clk_data) { int i; @@ -72,7 +72,7 @@ void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, } } -void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks, +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, struct clk_onecell_data *clk_data) { int i; @@ -95,7 +95,7 @@ void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks, } } -int __init mtk_clk_register_gates(struct device_node *node, +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, int num, struct clk_onecell_data *clk_data) { @@ -135,7 +135,7 @@ int __init mtk_clk_register_gates(struct device_node *node, return 0; } -struct clk * __init mtk_clk_register_composite(const struct mtk_composite *mc, +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, void __iomem *base, spinlock_t *lock) { struct clk *clk; @@ -222,7 +222,7 @@ err_out: return ERR_PTR(ret); } -void __init mtk_clk_register_composites(const struct mtk_composite *mcs, +void mtk_clk_register_composites(const struct mtk_composite *mcs, int num, void __iomem *base, spinlock_t *lock, struct clk_onecell_data *clk_data) { diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index 966cab1..0c2deac 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -313,7 +313,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, return clk; } -void __init mtk_clk_register_plls(struct device_node *node, +void mtk_clk_register_plls(struct device_node *node, const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) { void __iomem *base; diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 197e401..3495834 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -3,5 +3,5 @@ # obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o -obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b-clkc.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o +obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 53326c3..9bb70e7 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -98,7 +98,7 @@ struct meson_clk_mpll { }; #define MESON_GATE(_name, _reg, _bit) \ -struct clk_gate gxbb_##_name = { \ +struct clk_gate _name = { \ .reg = (void __iomem *) _reg, \ .bit_idx = (_bit), \ .lock = &clk_lock, \ diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c new file mode 100644 index 0000000..b45c5fb --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -0,0 +1,191 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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/>. + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/init.h> +#include <dt-bindings/clock/gxbb-aoclkc.h> +#include <dt-bindings/reset/gxbb-aoclkc.h> + +static DEFINE_SPINLOCK(gxbb_aoclk_lock); + +struct gxbb_aoclk_reset_controller { + struct reset_controller_dev reset; + unsigned int *data; + void __iomem *base; +}; + +static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct gxbb_aoclk_reset_controller *reset = + container_of(rcdev, struct gxbb_aoclk_reset_controller, reset); + + writel(BIT(reset->data[id]), reset->base); + + return 0; +} + +static const struct reset_control_ops gxbb_aoclk_reset_ops = { + .reset = gxbb_aoclk_do_reset, +}; + +#define GXBB_AO_GATE(_name, _bit) \ +static struct clk_gate _name##_ao = { \ + .reg = (void __iomem *)0, \ + .bit_idx = (_bit), \ + .lock = &gxbb_aoclk_lock, \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name "_ao", \ + .ops = &clk_gate_ops, \ + .parent_names = (const char *[]){ "clk81" }, \ + .num_parents = 1, \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ +} + +GXBB_AO_GATE(remote, 0); +GXBB_AO_GATE(i2c_master, 1); +GXBB_AO_GATE(i2c_slave, 2); +GXBB_AO_GATE(uart1, 3); +GXBB_AO_GATE(uart2, 5); +GXBB_AO_GATE(ir_blaster, 6); + +static unsigned int gxbb_aoclk_reset[] = { + [RESET_AO_REMOTE] = 16, + [RESET_AO_I2C_MASTER] = 18, + [RESET_AO_I2C_SLAVE] = 19, + [RESET_AO_UART1] = 17, + [RESET_AO_UART2] = 22, + [RESET_AO_IR_BLASTER] = 23, +}; + +static struct clk_gate *gxbb_aoclk_gate[] = { + [CLKID_AO_REMOTE] = &remote_ao, + [CLKID_AO_I2C_MASTER] = &i2c_master_ao, + [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao, + [CLKID_AO_UART1] = &uart1_ao, + [CLKID_AO_UART2] = &uart2_ao, + [CLKID_AO_IR_BLASTER] = &ir_blaster_ao, +}; + +static struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { + .hws = { + [CLKID_AO_REMOTE] = &remote_ao.hw, + [CLKID_AO_I2C_MASTER] = &i2c_master_ao.hw, + [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao.hw, + [CLKID_AO_UART1] = &uart1_ao.hw, + [CLKID_AO_UART2] = &uart2_ao.hw, + [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, + }, + .num = ARRAY_SIZE(gxbb_aoclk_gate), +}; + +static int gxbb_aoclkc_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + int ret, clkid; + struct device *dev = &pdev->dev; + struct gxbb_aoclk_reset_controller *rstc; + + rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return -ENOMEM; + + /* Generic clocks */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* Reset Controller */ + rstc->base = base; + rstc->data = gxbb_aoclk_reset; + rstc->reset.ops = &gxbb_aoclk_reset_ops; + rstc->reset.nr_resets = ARRAY_SIZE(gxbb_aoclk_reset); + rstc->reset.of_node = dev->of_node; + ret = devm_reset_controller_register(dev, &rstc->reset); + + /* + * Populate base address and register all clks + */ + for (clkid = 0; clkid < gxbb_aoclk_onecell_data.num; clkid++) { + gxbb_aoclk_gate[clkid]->reg = base; + + ret = devm_clk_hw_register(dev, + gxbb_aoclk_onecell_data.hws[clkid]); + if (ret) + return ret; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + &gxbb_aoclk_onecell_data); +} + +static const struct of_device_id gxbb_aoclkc_match_table[] = { + { .compatible = "amlogic,gxbb-aoclkc" }, + { } +}; + +static struct platform_driver gxbb_aoclkc_driver = { + .probe = gxbb_aoclkc_probe, + .driver = { + .name = "gxbb-aoclkc", + .of_match_table = gxbb_aoclkc_match_table, + }, +}; +builtin_platform_driver(gxbb_aoclkc_driver); diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index a4c6684b..9d9af44 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -565,90 +565,93 @@ static struct clk_gate gxbb_clk81 = { }; /* Everything Else (EE) domain gates */ -static MESON_GATE(ddr, HHI_GCLK_MPEG0, 0); -static MESON_GATE(dos, HHI_GCLK_MPEG0, 1); -static MESON_GATE(isa, HHI_GCLK_MPEG0, 5); -static MESON_GATE(pl301, HHI_GCLK_MPEG0, 6); -static MESON_GATE(periphs, HHI_GCLK_MPEG0, 7); -static MESON_GATE(spicc, HHI_GCLK_MPEG0, 8); -static MESON_GATE(i2c, HHI_GCLK_MPEG0, 9); -static MESON_GATE(sar_adc, HHI_GCLK_MPEG0, 10); -static MESON_GATE(smart_card, HHI_GCLK_MPEG0, 11); -static MESON_GATE(rng0, HHI_GCLK_MPEG0, 12); -static MESON_GATE(uart0, HHI_GCLK_MPEG0, 13); -static MESON_GATE(sdhc, HHI_GCLK_MPEG0, 14); -static MESON_GATE(stream, HHI_GCLK_MPEG0, 15); -static MESON_GATE(async_fifo, HHI_GCLK_MPEG0, 16); -static MESON_GATE(sdio, HHI_GCLK_MPEG0, 17); -static MESON_GATE(abuf, HHI_GCLK_MPEG0, 18); -static MESON_GATE(hiu_iface, HHI_GCLK_MPEG0, 19); -static MESON_GATE(assist_misc, HHI_GCLK_MPEG0, 23); -static MESON_GATE(spi, HHI_GCLK_MPEG0, 30); - -static MESON_GATE(i2s_spdif, HHI_GCLK_MPEG1, 2); -static MESON_GATE(eth, HHI_GCLK_MPEG1, 3); -static MESON_GATE(demux, HHI_GCLK_MPEG1, 4); -static MESON_GATE(aiu_glue, HHI_GCLK_MPEG1, 6); -static MESON_GATE(iec958, HHI_GCLK_MPEG1, 7); -static MESON_GATE(i2s_out, HHI_GCLK_MPEG1, 8); -static MESON_GATE(amclk, HHI_GCLK_MPEG1, 9); -static MESON_GATE(aififo2, HHI_GCLK_MPEG1, 10); -static MESON_GATE(mixer, HHI_GCLK_MPEG1, 11); -static MESON_GATE(mixer_iface, HHI_GCLK_MPEG1, 12); -static MESON_GATE(adc, HHI_GCLK_MPEG1, 13); -static MESON_GATE(blkmv, HHI_GCLK_MPEG1, 14); -static MESON_GATE(aiu, HHI_GCLK_MPEG1, 15); -static MESON_GATE(uart1, HHI_GCLK_MPEG1, 16); -static MESON_GATE(g2d, HHI_GCLK_MPEG1, 20); -static MESON_GATE(usb0, HHI_GCLK_MPEG1, 21); -static MESON_GATE(usb1, HHI_GCLK_MPEG1, 22); -static MESON_GATE(reset, HHI_GCLK_MPEG1, 23); -static MESON_GATE(nand, HHI_GCLK_MPEG1, 24); -static MESON_GATE(dos_parser, HHI_GCLK_MPEG1, 25); -static MESON_GATE(usb, HHI_GCLK_MPEG1, 26); -static MESON_GATE(vdin1, HHI_GCLK_MPEG1, 28); -static MESON_GATE(ahb_arb0, HHI_GCLK_MPEG1, 29); -static MESON_GATE(efuse, HHI_GCLK_MPEG1, 30); -static MESON_GATE(boot_rom, HHI_GCLK_MPEG1, 31); - -static MESON_GATE(ahb_data_bus, HHI_GCLK_MPEG2, 1); -static MESON_GATE(ahb_ctrl_bus, HHI_GCLK_MPEG2, 2); -static MESON_GATE(hdmi_intr_sync, HHI_GCLK_MPEG2, 3); -static MESON_GATE(hdmi_pclk, HHI_GCLK_MPEG2, 4); -static MESON_GATE(usb1_ddr_bridge, HHI_GCLK_MPEG2, 8); -static MESON_GATE(usb0_ddr_bridge, HHI_GCLK_MPEG2, 9); -static MESON_GATE(mmc_pclk, HHI_GCLK_MPEG2, 11); -static MESON_GATE(dvin, HHI_GCLK_MPEG2, 12); -static MESON_GATE(uart2, HHI_GCLK_MPEG2, 15); -static MESON_GATE(sana, HHI_GCLK_MPEG2, 22); -static MESON_GATE(vpu_intr, HHI_GCLK_MPEG2, 25); -static MESON_GATE(sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26); -static MESON_GATE(clk81_a53, HHI_GCLK_MPEG2, 29); - -static MESON_GATE(vclk2_venci0, HHI_GCLK_OTHER, 1); -static MESON_GATE(vclk2_venci1, HHI_GCLK_OTHER, 2); -static MESON_GATE(vclk2_vencp0, HHI_GCLK_OTHER, 3); -static MESON_GATE(vclk2_vencp1, HHI_GCLK_OTHER, 4); -static MESON_GATE(gclk_venci_int0, HHI_GCLK_OTHER, 8); -static MESON_GATE(gclk_vencp_int, HHI_GCLK_OTHER, 9); -static MESON_GATE(dac_clk, HHI_GCLK_OTHER, 10); -static MESON_GATE(aoclk_gate, HHI_GCLK_OTHER, 14); -static MESON_GATE(iec958_gate, HHI_GCLK_OTHER, 16); -static MESON_GATE(enc480p, HHI_GCLK_OTHER, 20); -static MESON_GATE(rng1, HHI_GCLK_OTHER, 21); -static MESON_GATE(gclk_venci_int1, HHI_GCLK_OTHER, 22); -static MESON_GATE(vclk2_venclmcc, HHI_GCLK_OTHER, 24); -static MESON_GATE(vclk2_vencl, HHI_GCLK_OTHER, 25); -static MESON_GATE(vclk_other, HHI_GCLK_OTHER, 26); -static MESON_GATE(edp, HHI_GCLK_OTHER, 31); +static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); +static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); +static MESON_GATE(gxbb_isa, HHI_GCLK_MPEG0, 5); +static MESON_GATE(gxbb_pl301, HHI_GCLK_MPEG0, 6); +static MESON_GATE(gxbb_periphs, HHI_GCLK_MPEG0, 7); +static MESON_GATE(gxbb_spicc, HHI_GCLK_MPEG0, 8); +static MESON_GATE(gxbb_i2c, HHI_GCLK_MPEG0, 9); +static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG0, 10); +static MESON_GATE(gxbb_smart_card, HHI_GCLK_MPEG0, 11); +static MESON_GATE(gxbb_rng0, HHI_GCLK_MPEG0, 12); +static MESON_GATE(gxbb_uart0, HHI_GCLK_MPEG0, 13); +static MESON_GATE(gxbb_sdhc, HHI_GCLK_MPEG0, 14); +static MESON_GATE(gxbb_stream, HHI_GCLK_MPEG0, 15); +static MESON_GATE(gxbb_async_fifo, HHI_GCLK_MPEG0, 16); +static MESON_GATE(gxbb_sdio, HHI_GCLK_MPEG0, 17); +static MESON_GATE(gxbb_abuf, HHI_GCLK_MPEG0, 18); +static MESON_GATE(gxbb_hiu_iface, HHI_GCLK_MPEG0, 19); +static MESON_GATE(gxbb_assist_misc, HHI_GCLK_MPEG0, 23); +static MESON_GATE(gxbb_emmc_a, HHI_GCLK_MPEG0, 24); +static MESON_GATE(gxbb_emmc_b, HHI_GCLK_MPEG0, 25); +static MESON_GATE(gxbb_emmc_c, HHI_GCLK_MPEG0, 26); +static MESON_GATE(gxbb_spi, HHI_GCLK_MPEG0, 30); + +static MESON_GATE(gxbb_i2s_spdif, HHI_GCLK_MPEG1, 2); +static MESON_GATE(gxbb_eth, HHI_GCLK_MPEG1, 3); +static MESON_GATE(gxbb_demux, HHI_GCLK_MPEG1, 4); +static MESON_GATE(gxbb_aiu_glue, HHI_GCLK_MPEG1, 6); +static MESON_GATE(gxbb_iec958, HHI_GCLK_MPEG1, 7); +static MESON_GATE(gxbb_i2s_out, HHI_GCLK_MPEG1, 8); +static MESON_GATE(gxbb_amclk, HHI_GCLK_MPEG1, 9); +static MESON_GATE(gxbb_aififo2, HHI_GCLK_MPEG1, 10); +static MESON_GATE(gxbb_mixer, HHI_GCLK_MPEG1, 11); +static MESON_GATE(gxbb_mixer_iface, HHI_GCLK_MPEG1, 12); +static MESON_GATE(gxbb_adc, HHI_GCLK_MPEG1, 13); +static MESON_GATE(gxbb_blkmv, HHI_GCLK_MPEG1, 14); +static MESON_GATE(gxbb_aiu, HHI_GCLK_MPEG1, 15); +static MESON_GATE(gxbb_uart1, HHI_GCLK_MPEG1, 16); +static MESON_GATE(gxbb_g2d, HHI_GCLK_MPEG1, 20); +static MESON_GATE(gxbb_usb0, HHI_GCLK_MPEG1, 21); +static MESON_GATE(gxbb_usb1, HHI_GCLK_MPEG1, 22); +static MESON_GATE(gxbb_reset, HHI_GCLK_MPEG1, 23); +static MESON_GATE(gxbb_nand, HHI_GCLK_MPEG1, 24); +static MESON_GATE(gxbb_dos_parser, HHI_GCLK_MPEG1, 25); +static MESON_GATE(gxbb_usb, HHI_GCLK_MPEG1, 26); +static MESON_GATE(gxbb_vdin1, HHI_GCLK_MPEG1, 28); +static MESON_GATE(gxbb_ahb_arb0, HHI_GCLK_MPEG1, 29); +static MESON_GATE(gxbb_efuse, HHI_GCLK_MPEG1, 30); +static MESON_GATE(gxbb_boot_rom, HHI_GCLK_MPEG1, 31); + +static MESON_GATE(gxbb_ahb_data_bus, HHI_GCLK_MPEG2, 1); +static MESON_GATE(gxbb_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2); +static MESON_GATE(gxbb_hdmi_intr_sync, HHI_GCLK_MPEG2, 3); +static MESON_GATE(gxbb_hdmi_pclk, HHI_GCLK_MPEG2, 4); +static MESON_GATE(gxbb_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8); +static MESON_GATE(gxbb_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9); +static MESON_GATE(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11); +static MESON_GATE(gxbb_dvin, HHI_GCLK_MPEG2, 12); +static MESON_GATE(gxbb_uart2, HHI_GCLK_MPEG2, 15); +static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG2, 22); +static MESON_GATE(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25); +static MESON_GATE(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26); +static MESON_GATE(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29); + +static MESON_GATE(gxbb_vclk2_venci0, HHI_GCLK_OTHER, 1); +static MESON_GATE(gxbb_vclk2_venci1, HHI_GCLK_OTHER, 2); +static MESON_GATE(gxbb_vclk2_vencp0, HHI_GCLK_OTHER, 3); +static MESON_GATE(gxbb_vclk2_vencp1, HHI_GCLK_OTHER, 4); +static MESON_GATE(gxbb_gclk_venci_int0, HHI_GCLK_OTHER, 8); +static MESON_GATE(gxbb_gclk_vencp_int, HHI_GCLK_OTHER, 9); +static MESON_GATE(gxbb_dac_clk, HHI_GCLK_OTHER, 10); +static MESON_GATE(gxbb_aoclk_gate, HHI_GCLK_OTHER, 14); +static MESON_GATE(gxbb_iec958_gate, HHI_GCLK_OTHER, 16); +static MESON_GATE(gxbb_enc480p, HHI_GCLK_OTHER, 20); +static MESON_GATE(gxbb_rng1, HHI_GCLK_OTHER, 21); +static MESON_GATE(gxbb_gclk_venci_int1, HHI_GCLK_OTHER, 22); +static MESON_GATE(gxbb_vclk2_venclmcc, HHI_GCLK_OTHER, 24); +static MESON_GATE(gxbb_vclk2_vencl, HHI_GCLK_OTHER, 25); +static MESON_GATE(gxbb_vclk_other, HHI_GCLK_OTHER, 26); +static MESON_GATE(gxbb_edp, HHI_GCLK_OTHER, 31); /* Always On (AO) domain gates */ -static MESON_GATE(ao_media_cpu, HHI_GCLK_AO, 0); -static MESON_GATE(ao_ahb_sram, HHI_GCLK_AO, 1); -static MESON_GATE(ao_ahb_bus, HHI_GCLK_AO, 2); -static MESON_GATE(ao_iface, HHI_GCLK_AO, 3); -static MESON_GATE(ao_i2c, HHI_GCLK_AO, 4); +static MESON_GATE(gxbb_ao_media_cpu, HHI_GCLK_AO, 0); +static MESON_GATE(gxbb_ao_ahb_sram, HHI_GCLK_AO, 1); +static MESON_GATE(gxbb_ao_ahb_bus, HHI_GCLK_AO, 2); +static MESON_GATE(gxbb_ao_iface, HHI_GCLK_AO, 3); +static MESON_GATE(gxbb_ao_i2c, HHI_GCLK_AO, 4); /* Array of all clocks provided by this provider */ @@ -748,6 +751,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw, [CLKID_AO_IFACE] = &gxbb_ao_iface.hw, [CLKID_AO_I2C] = &gxbb_ao_i2c.hw, + [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw, + [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw, + [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw, }, .num = NR_CLKS, }; @@ -847,6 +853,9 @@ static struct clk_gate *gxbb_clk_gates[] = { &gxbb_ao_ahb_bus, &gxbb_ao_iface, &gxbb_ao_i2c, + &gxbb_emmc_a, + &gxbb_emmc_b, + &gxbb_emmc_c, }; static int gxbb_clkc_probe(struct platform_device *pdev) @@ -937,8 +946,4 @@ static struct platform_driver gxbb_driver = { }, }; -static int __init gxbb_clkc_init(void) -{ - return platform_driver_register(&gxbb_driver); -} -device_initcall(gxbb_clkc_init); +builtin_platform_driver(gxbb_driver); diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index a2adf34..ae461b1 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -170,11 +170,11 @@ */ #define CLKID_SYS_PLL 0 /* CLKID_CPUCLK */ -#define CLKID_HDMI_PLL 2 +/* CLKID_HDMI_PLL */ #define CLKID_FIXED_PLL 3 -#define CLKID_FCLK_DIV2 4 -#define CLKID_FCLK_DIV3 5 -#define CLKID_FCLK_DIV4 6 +/* CLKID_FCLK_DIV2 */ +/* CLKID_FCLK_DIV3 */ +/* CLKID_FCLK_DIV4 */ #define CLKID_FCLK_DIV5 7 #define CLKID_FCLK_DIV7 8 #define CLKID_GP0_PLL 9 @@ -262,8 +262,11 @@ #define CLKID_AO_AHB_BUS 91 #define CLKID_AO_IFACE 92 #define CLKID_AO_I2C 93 +/* CLKID_SD_EMMC_A */ +/* CLKID_SD_EMMC_B */ +/* CLKID_SD_EMMC_C */ -#define NR_CLKS 94 +#define NR_CLKS 97 /* include the CLKIDs that have been made part of the stable DT binding */ #include <dt-bindings/clock/gxbb-clkc.h> diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b.c index 4c9413c..3f1be46 100644 --- a/drivers/clk/meson/meson8b-clkc.c +++ b/drivers/clk/meson/meson8b.c @@ -23,27 +23,11 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/of_address.h> -#include <dt-bindings/clock/meson8b-clkc.h> #include <linux/platform_device.h> #include <linux/init.h> #include "clkc.h" - -/* - * Clock controller register offsets - * - * Register offsets from the HardKernel[0] data sheet are listed in comment - * blocks below. Those offsets must be multiplied by 4 before adding them to - * the base address to get the right value - * - * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf - */ -#define MESON8B_REG_SYS_CPU_CNTL1 0x015c /* 0x57 offset in data sheet */ -#define MESON8B_REG_HHI_MPEG 0x0174 /* 0x5d offset in data sheet */ -#define MESON8B_REG_MALI 0x01b0 /* 0x6c offset in data sheet */ -#define MESON8B_REG_PLL_FIXED 0x0280 -#define MESON8B_REG_PLL_SYS 0x0300 -#define MESON8B_REG_PLL_VID 0x0320 +#include "meson8b.h" static DEFINE_SPINLOCK(clk_lock); @@ -128,17 +112,17 @@ static struct clk_fixed_rate meson8b_xtal = { static struct meson_clk_pll meson8b_fixed_pll = { .m = { - .reg_off = MESON8B_REG_PLL_FIXED, + .reg_off = HHI_MPLL_CNTL, .shift = 0, .width = 9, }, .n = { - .reg_off = MESON8B_REG_PLL_FIXED, + .reg_off = HHI_MPLL_CNTL, .shift = 9, .width = 5, }, .od = { - .reg_off = MESON8B_REG_PLL_FIXED, + .reg_off = HHI_MPLL_CNTL, .shift = 16, .width = 2, }, @@ -154,17 +138,17 @@ static struct meson_clk_pll meson8b_fixed_pll = { static struct meson_clk_pll meson8b_vid_pll = { .m = { - .reg_off = MESON8B_REG_PLL_VID, + .reg_off = HHI_VID_PLL_CNTL, .shift = 0, .width = 9, }, .n = { - .reg_off = MESON8B_REG_PLL_VID, + .reg_off = HHI_VID_PLL_CNTL, .shift = 9, .width = 5, }, .od = { - .reg_off = MESON8B_REG_PLL_VID, + .reg_off = HHI_VID_PLL_CNTL, .shift = 16, .width = 2, }, @@ -180,17 +164,17 @@ static struct meson_clk_pll meson8b_vid_pll = { static struct meson_clk_pll meson8b_sys_pll = { .m = { - .reg_off = MESON8B_REG_PLL_SYS, + .reg_off = HHI_SYS_PLL_CNTL, .shift = 0, .width = 9, }, .n = { - .reg_off = MESON8B_REG_PLL_SYS, + .reg_off = HHI_SYS_PLL_CNTL, .shift = 9, .width = 5, }, .od = { - .reg_off = MESON8B_REG_PLL_SYS, + .reg_off = HHI_SYS_PLL_CNTL, .shift = 16, .width = 2, }, @@ -267,7 +251,7 @@ static struct clk_fixed_factor meson8b_fclk_div7 = { * forthcoming coordinated clock rates feature */ static struct meson_clk_cpu meson8b_cpu_clk = { - .reg_off = MESON8B_REG_SYS_CPU_CNTL1, + .reg_off = HHI_SYS_CPU_CLK_CNTL1, .div_table = cpu_div_table, .clk_nb.notifier_call = meson_clk_cpu_notifier_cb, .hw.init = &(struct clk_init_data){ @@ -281,7 +265,7 @@ static struct meson_clk_cpu meson8b_cpu_clk = { static u32 mux_table_clk81[] = { 6, 5, 7 }; struct clk_mux meson8b_mpeg_clk_sel = { - .reg = (void *)MESON8B_REG_HHI_MPEG, + .reg = (void *)HHI_MPEG_CLK_CNTL, .mask = 0x7, .shift = 12, .flags = CLK_MUX_READ_ONLY, @@ -303,7 +287,7 @@ struct clk_mux meson8b_mpeg_clk_sel = { }; struct clk_divider meson8b_mpeg_clk_div = { - .reg = (void *)MESON8B_REG_HHI_MPEG, + .reg = (void *)HHI_MPEG_CLK_CNTL, .shift = 0, .width = 7, .lock = &clk_lock, @@ -317,7 +301,7 @@ struct clk_divider meson8b_mpeg_clk_div = { }; struct clk_gate meson8b_clk81 = { - .reg = (void *)MESON8B_REG_HHI_MPEG, + .reg = (void *)HHI_MPEG_CLK_CNTL, .bit_idx = 7, .lock = &clk_lock, .hw.init = &(struct clk_init_data){ @@ -329,6 +313,92 @@ struct clk_gate meson8b_clk81 = { }, }; +/* Everything Else (EE) domain gates */ + +static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0); +static MESON_GATE(meson8b_dos, HHI_GCLK_MPEG0, 1); +static MESON_GATE(meson8b_isa, HHI_GCLK_MPEG0, 5); +static MESON_GATE(meson8b_pl301, HHI_GCLK_MPEG0, 6); +static MESON_GATE(meson8b_periphs, HHI_GCLK_MPEG0, 7); +static MESON_GATE(meson8b_spicc, HHI_GCLK_MPEG0, 8); +static MESON_GATE(meson8b_i2c, HHI_GCLK_MPEG0, 9); +static MESON_GATE(meson8b_sar_adc, HHI_GCLK_MPEG0, 10); +static MESON_GATE(meson8b_smart_card, HHI_GCLK_MPEG0, 11); +static MESON_GATE(meson8b_rng0, HHI_GCLK_MPEG0, 12); +static MESON_GATE(meson8b_uart0, HHI_GCLK_MPEG0, 13); +static MESON_GATE(meson8b_sdhc, HHI_GCLK_MPEG0, 14); +static MESON_GATE(meson8b_stream, HHI_GCLK_MPEG0, 15); +static MESON_GATE(meson8b_async_fifo, HHI_GCLK_MPEG0, 16); +static MESON_GATE(meson8b_sdio, HHI_GCLK_MPEG0, 17); +static MESON_GATE(meson8b_abuf, HHI_GCLK_MPEG0, 18); +static MESON_GATE(meson8b_hiu_iface, HHI_GCLK_MPEG0, 19); +static MESON_GATE(meson8b_assist_misc, HHI_GCLK_MPEG0, 23); +static MESON_GATE(meson8b_spi, HHI_GCLK_MPEG0, 30); + +static MESON_GATE(meson8b_i2s_spdif, HHI_GCLK_MPEG1, 2); +static MESON_GATE(meson8b_eth, HHI_GCLK_MPEG1, 3); +static MESON_GATE(meson8b_demux, HHI_GCLK_MPEG1, 4); +static MESON_GATE(meson8b_aiu_glue, HHI_GCLK_MPEG1, 6); +static MESON_GATE(meson8b_iec958, HHI_GCLK_MPEG1, 7); +static MESON_GATE(meson8b_i2s_out, HHI_GCLK_MPEG1, 8); +static MESON_GATE(meson8b_amclk, HHI_GCLK_MPEG1, 9); +static MESON_GATE(meson8b_aififo2, HHI_GCLK_MPEG1, 10); +static MESON_GATE(meson8b_mixer, HHI_GCLK_MPEG1, 11); +static MESON_GATE(meson8b_mixer_iface, HHI_GCLK_MPEG1, 12); +static MESON_GATE(meson8b_adc, HHI_GCLK_MPEG1, 13); +static MESON_GATE(meson8b_blkmv, HHI_GCLK_MPEG1, 14); +static MESON_GATE(meson8b_aiu, HHI_GCLK_MPEG1, 15); +static MESON_GATE(meson8b_uart1, HHI_GCLK_MPEG1, 16); +static MESON_GATE(meson8b_g2d, HHI_GCLK_MPEG1, 20); +static MESON_GATE(meson8b_usb0, HHI_GCLK_MPEG1, 21); +static MESON_GATE(meson8b_usb1, HHI_GCLK_MPEG1, 22); +static MESON_GATE(meson8b_reset, HHI_GCLK_MPEG1, 23); +static MESON_GATE(meson8b_nand, HHI_GCLK_MPEG1, 24); +static MESON_GATE(meson8b_dos_parser, HHI_GCLK_MPEG1, 25); +static MESON_GATE(meson8b_usb, HHI_GCLK_MPEG1, 26); +static MESON_GATE(meson8b_vdin1, HHI_GCLK_MPEG1, 28); +static MESON_GATE(meson8b_ahb_arb0, HHI_GCLK_MPEG1, 29); +static MESON_GATE(meson8b_efuse, HHI_GCLK_MPEG1, 30); +static MESON_GATE(meson8b_boot_rom, HHI_GCLK_MPEG1, 31); + +static MESON_GATE(meson8b_ahb_data_bus, HHI_GCLK_MPEG2, 1); +static MESON_GATE(meson8b_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2); +static MESON_GATE(meson8b_hdmi_intr_sync, HHI_GCLK_MPEG2, 3); +static MESON_GATE(meson8b_hdmi_pclk, HHI_GCLK_MPEG2, 4); +static MESON_GATE(meson8b_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8); +static MESON_GATE(meson8b_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9); +static MESON_GATE(meson8b_mmc_pclk, HHI_GCLK_MPEG2, 11); +static MESON_GATE(meson8b_dvin, HHI_GCLK_MPEG2, 12); +static MESON_GATE(meson8b_uart2, HHI_GCLK_MPEG2, 15); +static MESON_GATE(meson8b_sana, HHI_GCLK_MPEG2, 22); +static MESON_GATE(meson8b_vpu_intr, HHI_GCLK_MPEG2, 25); +static MESON_GATE(meson8b_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26); +static MESON_GATE(meson8b_clk81_a9, HHI_GCLK_MPEG2, 29); + +static MESON_GATE(meson8b_vclk2_venci0, HHI_GCLK_OTHER, 1); +static MESON_GATE(meson8b_vclk2_venci1, HHI_GCLK_OTHER, 2); +static MESON_GATE(meson8b_vclk2_vencp0, HHI_GCLK_OTHER, 3); +static MESON_GATE(meson8b_vclk2_vencp1, HHI_GCLK_OTHER, 4); +static MESON_GATE(meson8b_gclk_venci_int, HHI_GCLK_OTHER, 8); +static MESON_GATE(meson8b_gclk_vencp_int, HHI_GCLK_OTHER, 9); +static MESON_GATE(meson8b_dac_clk, HHI_GCLK_OTHER, 10); +static MESON_GATE(meson8b_aoclk_gate, HHI_GCLK_OTHER, 14); +static MESON_GATE(meson8b_iec958_gate, HHI_GCLK_OTHER, 16); +static MESON_GATE(meson8b_enc480p, HHI_GCLK_OTHER, 20); +static MESON_GATE(meson8b_rng1, HHI_GCLK_OTHER, 21); +static MESON_GATE(meson8b_gclk_vencl_int, HHI_GCLK_OTHER, 22); +static MESON_GATE(meson8b_vclk2_venclmcc, HHI_GCLK_OTHER, 24); +static MESON_GATE(meson8b_vclk2_vencl, HHI_GCLK_OTHER, 25); +static MESON_GATE(meson8b_vclk2_other, HHI_GCLK_OTHER, 26); +static MESON_GATE(meson8b_edp, HHI_GCLK_OTHER, 31); + +/* Always On (AO) domain gates */ + +static MESON_GATE(meson8b_ao_media_cpu, HHI_GCLK_AO, 0); +static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1); +static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2); +static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3); + static struct clk_hw_onecell_data meson8b_hw_onecell_data = { .hws = { [CLKID_XTAL] = &meson8b_xtal.hw, @@ -344,6 +414,83 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw, [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw, [CLKID_CLK81] = &meson8b_clk81.hw, + [CLKID_DDR] = &meson8b_ddr.hw, + [CLKID_DOS] = &meson8b_dos.hw, + [CLKID_ISA] = &meson8b_isa.hw, + [CLKID_PL301] = &meson8b_pl301.hw, + [CLKID_PERIPHS] = &meson8b_periphs.hw, + [CLKID_SPICC] = &meson8b_spicc.hw, + [CLKID_I2C] = &meson8b_i2c.hw, + [CLKID_SAR_ADC] = &meson8b_sar_adc.hw, + [CLKID_SMART_CARD] = &meson8b_smart_card.hw, + [CLKID_RNG0] = &meson8b_rng0.hw, + [CLKID_UART0] = &meson8b_uart0.hw, + [CLKID_SDHC] = &meson8b_sdhc.hw, + [CLKID_STREAM] = &meson8b_stream.hw, + [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw, + [CLKID_SDIO] = &meson8b_sdio.hw, + [CLKID_ABUF] = &meson8b_abuf.hw, + [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw, + [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw, + [CLKID_SPI] = &meson8b_spi.hw, + [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw, + [CLKID_ETH] = &meson8b_eth.hw, + [CLKID_DEMUX] = &meson8b_demux.hw, + [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw, + [CLKID_IEC958] = &meson8b_iec958.hw, + [CLKID_I2S_OUT] = &meson8b_i2s_out.hw, + [CLKID_AMCLK] = &meson8b_amclk.hw, + [CLKID_AIFIFO2] = &meson8b_aififo2.hw, + [CLKID_MIXER] = &meson8b_mixer.hw, + [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw, + [CLKID_ADC] = &meson8b_adc.hw, + [CLKID_BLKMV] = &meson8b_blkmv.hw, + [CLKID_AIU] = &meson8b_aiu.hw, + [CLKID_UART1] = &meson8b_uart1.hw, + [CLKID_G2D] = &meson8b_g2d.hw, + [CLKID_USB0] = &meson8b_usb0.hw, + [CLKID_USB1] = &meson8b_usb1.hw, + [CLKID_RESET] = &meson8b_reset.hw, + [CLKID_NAND] = &meson8b_nand.hw, + [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw, + [CLKID_USB] = &meson8b_usb.hw, + [CLKID_VDIN1] = &meson8b_vdin1.hw, + [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw, + [CLKID_EFUSE] = &meson8b_efuse.hw, + [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw, + [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw, + [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw, + [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw, + [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw, + [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw, + [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw, + [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw, + [CLKID_DVIN] = &meson8b_dvin.hw, + [CLKID_UART2] = &meson8b_uart2.hw, + [CLKID_SANA] = &meson8b_sana.hw, + [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw, + [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw, + [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw, + [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw, + [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw, + [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw, + [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw, + [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw, + [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw, + [CLKID_DAC_CLK] = &meson8b_dac_clk.hw, + [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw, + [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw, + [CLKID_ENC480P] = &meson8b_enc480p.hw, + [CLKID_RNG1] = &meson8b_rng1.hw, + [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw, + [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw, + [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw, + [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw, + [CLKID_EDP] = &meson8b_edp.hw, + [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw, + [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw, + [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw, + [CLKID_AO_IFACE] = &meson8b_ao_iface.hw, }, .num = CLK_NR_CLKS, }; @@ -354,6 +501,87 @@ static struct meson_clk_pll *const meson8b_clk_plls[] = { &meson8b_sys_pll, }; +static struct clk_gate *meson8b_clk_gates[] = { + &meson8b_clk81, + &meson8b_ddr, + &meson8b_dos, + &meson8b_isa, + &meson8b_pl301, + &meson8b_periphs, + &meson8b_spicc, + &meson8b_i2c, + &meson8b_sar_adc, + &meson8b_smart_card, + &meson8b_rng0, + &meson8b_uart0, + &meson8b_sdhc, + &meson8b_stream, + &meson8b_async_fifo, + &meson8b_sdio, + &meson8b_abuf, + &meson8b_hiu_iface, + &meson8b_assist_misc, + &meson8b_spi, + &meson8b_i2s_spdif, + &meson8b_eth, + &meson8b_demux, + &meson8b_aiu_glue, + &meson8b_iec958, + &meson8b_i2s_out, + &meson8b_amclk, + &meson8b_aififo2, + &meson8b_mixer, + &meson8b_mixer_iface, + &meson8b_adc, + &meson8b_blkmv, + &meson8b_aiu, + &meson8b_uart1, + &meson8b_g2d, + &meson8b_usb0, + &meson8b_usb1, + &meson8b_reset, + &meson8b_nand, + &meson8b_dos_parser, + &meson8b_usb, + &meson8b_vdin1, + &meson8b_ahb_arb0, + &meson8b_efuse, + &meson8b_boot_rom, + &meson8b_ahb_data_bus, + &meson8b_ahb_ctrl_bus, + &meson8b_hdmi_intr_sync, + &meson8b_hdmi_pclk, + &meson8b_usb1_ddr_bridge, + &meson8b_usb0_ddr_bridge, + &meson8b_mmc_pclk, + &meson8b_dvin, + &meson8b_uart2, + &meson8b_sana, + &meson8b_vpu_intr, + &meson8b_sec_ahb_ahb3_bridge, + &meson8b_clk81_a9, + &meson8b_vclk2_venci0, + &meson8b_vclk2_venci1, + &meson8b_vclk2_vencp0, + &meson8b_vclk2_vencp1, + &meson8b_gclk_venci_int, + &meson8b_gclk_vencp_int, + &meson8b_dac_clk, + &meson8b_aoclk_gate, + &meson8b_iec958_gate, + &meson8b_enc480p, + &meson8b_rng1, + &meson8b_gclk_vencl_int, + &meson8b_vclk2_venclmcc, + &meson8b_vclk2_vencl, + &meson8b_vclk2_other, + &meson8b_edp, + &meson8b_ao_media_cpu, + &meson8b_ao_ahb_sram, + &meson8b_ao_ahb_bus, + &meson8b_ao_iface, +}; + static int meson8b_clkc_probe(struct platform_device *pdev) { void __iomem *clk_base; @@ -381,6 +609,11 @@ static int meson8b_clkc_probe(struct platform_device *pdev) meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg; meson8b_clk81.reg = clk_base + (u32)meson8b_clk81.reg; + /* Populate base address for gates */ + for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++) + meson8b_clk_gates[i]->reg = clk_base + + (u32)meson8b_clk_gates[i]->reg; + /* * register all clks * CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1 @@ -440,8 +673,4 @@ static struct platform_driver meson8b_driver = { }, }; -static int __init meson8b_clkc_init(void) -{ - return platform_driver_register(&meson8b_driver); -} -device_initcall(meson8b_clkc_init); +builtin_platform_driver(meson8b_driver); diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h new file mode 100644 index 0000000..010e958 --- /dev/null +++ b/drivers/clk/meson/meson8b.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * Copyright (c) 2016 BayLibre, Inc. + * Michael Turquette <mturquette@baylibre.com> + * + * 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. + * + * This program is distributed in the hope 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 __MESON8B_H +#define __MESON8B_H + +/* + * Clock controller register offsets + * + * Register offsets from the HardKernel[0] data sheet are listed in comment + * blocks below. Those offsets must be multiplied by 4 before adding them to + * the base address to get the right value + * + * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf + */ +#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */ +#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */ +#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ +#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */ +#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */ +#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */ +#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */ +#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ +#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */ +#define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * Migrate them out of this header and into the DT header file when they need + * to be exposed to client nodes in DT: include/dt-bindings/clock/meson8b-clkc.h + */ + +/* CLKID_UNUSED */ +/* CLKID_XTAL */ +/* CLKID_PLL_FIXED */ +/* CLKID_PLL_VID */ +/* CLKID_PLL_SYS */ +/* CLKID_FCLK_DIV2 */ +/* CLKID_FCLK_DIV3 */ +/* CLKID_FCLK_DIV4 */ +/* CLKID_FCLK_DIV5 */ +/* CLKID_FCLK_DIV7 */ +/* CLKID_CLK81 */ +/* CLKID_MALI */ +/* CLKID_CPUCLK */ +/* CLKID_ZERO */ +/* CLKID_MPEG_SEL */ +/* CLKID_MPEG_DIV */ +#define CLKID_DDR 16 +#define CLKID_DOS 17 +#define CLKID_ISA 18 +#define CLKID_PL301 19 +#define CLKID_PERIPHS 20 +#define CLKID_SPICC 21 +#define CLKID_I2C 22 +#define CLKID_SAR_ADC 23 +#define CLKID_SMART_CARD 24 +#define CLKID_RNG0 25 +#define CLKID_UART0 26 +#define CLKID_SDHC 27 +#define CLKID_STREAM 28 +#define CLKID_ASYNC_FIFO 29 +#define CLKID_SDIO 30 +#define CLKID_ABUF 31 +#define CLKID_HIU_IFACE 32 +#define CLKID_ASSIST_MISC 33 +#define CLKID_SPI 34 +#define CLKID_I2S_SPDIF 35 +#define CLKID_ETH 36 +#define CLKID_DEMUX 37 +#define CLKID_AIU_GLUE 38 +#define CLKID_IEC958 39 +#define CLKID_I2S_OUT 40 +#define CLKID_AMCLK 41 +#define CLKID_AIFIFO2 42 +#define CLKID_MIXER 43 +#define CLKID_MIXER_IFACE 44 +#define CLKID_ADC 45 +#define CLKID_BLKMV 46 +#define CLKID_AIU 47 +#define CLKID_UART1 48 +#define CLKID_G2D 49 +#define CLKID_USB0 50 +#define CLKID_USB1 51 +#define CLKID_RESET 52 +#define CLKID_NAND 53 +#define CLKID_DOS_PARSER 54 +#define CLKID_USB 55 +#define CLKID_VDIN1 56 +#define CLKID_AHB_ARB0 57 +#define CLKID_EFUSE 58 +#define CLKID_BOOT_ROM 59 +#define CLKID_AHB_DATA_BUS 60 +#define CLKID_AHB_CTRL_BUS 61 +#define CLKID_HDMI_INTR_SYNC 62 +#define CLKID_HDMI_PCLK 63 +#define CLKID_USB1_DDR_BRIDGE 64 +#define CLKID_USB0_DDR_BRIDGE 65 +#define CLKID_MMC_PCLK 66 +#define CLKID_DVIN 67 +#define CLKID_UART2 68 +#define CLKID_SANA 69 +#define CLKID_VPU_INTR 70 +#define CLKID_SEC_AHB_AHB3_BRIDGE 71 +#define CLKID_CLK81_A9 72 +#define CLKID_VCLK2_VENCI0 73 +#define CLKID_VCLK2_VENCI1 74 +#define CLKID_VCLK2_VENCP0 75 +#define CLKID_VCLK2_VENCP1 76 +#define CLKID_GCLK_VENCI_INT 77 +#define CLKID_GCLK_VENCP_INT 78 +#define CLKID_DAC_CLK 79 +#define CLKID_AOCLK_GATE 80 +#define CLKID_IEC958_GATE 81 +#define CLKID_ENC480P 82 +#define CLKID_RNG1 83 +#define CLKID_GCLK_VENCL_INT 84 +#define CLKID_VCLK2_VENCLMCC 85 +#define CLKID_VCLK2_VENCL 86 +#define CLKID_VCLK2_OTHER 87 +#define CLKID_EDP 88 +#define CLKID_AO_MEDIA_CPU 89 +#define CLKID_AO_AHB_SRAM 90 +#define CLKID_AO_AHB_BUS 91 +#define CLKID_AO_IFACE 92 + +#define CLK_NR_CLKS 93 + +/* include the CLKIDs that have been made part of the stable DT binding */ +#include <dt-bindings/clock/meson8b-clkc.h> + +#endif /* __MESON8B_H */ diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-core.c index ca85cea..c3b3014 100644 --- a/drivers/clk/microchip/clk-core.c +++ b/drivers/clk/microchip/clk-core.c @@ -199,9 +199,9 @@ static int pbclk_set_rate(struct clk_hw *hw, unsigned long rate, spin_unlock_irqrestore(&pb->core->reg_lock, flags); - /* wait again, for pbdivready */ - err = readl_poll_timeout_atomic(pb->ctrl_reg, v, v & PB_DIV_READY, - 1, LOCK_TIMEOUT_US); + /* wait again for DIV_READY */ + err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY, + 1, LOCK_TIMEOUT_US); if (err) return err; diff --git a/drivers/clk/microchip/clk-pic32mzda.c b/drivers/clk/microchip/clk-pic32mzda.c index 51f5438..9f73477 100644 --- a/drivers/clk/microchip/clk-pic32mzda.c +++ b/drivers/clk/microchip/clk-pic32mzda.c @@ -118,6 +118,7 @@ static const struct pic32_sec_osc_data sosc_clk = { .status_reg = 0x1d0, .enable_mask = BIT(1), .status_mask = BIT(4), + .fixed_rate = 32768, .init_data = { .name = "sosc_clk", .parent_names = NULL, diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 383f6a4..0380234 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -16,6 +16,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/clk/mmp.h> #include "clk.h" diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 3165da7..fddc8ac 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -24,6 +24,9 @@ config ARMADA_39X_CLK bool select MVEBU_CLK_COMMON +config ARMADA_37XX_CLK + bool + config ARMADA_XP_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 7172ef6..d9ae97f 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -6,6 +6,9 @@ obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o +obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o +obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o +obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c new file mode 100644 index 0000000..45905fc --- /dev/null +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -0,0 +1,447 @@ +/* + * Marvell Armada 37xx SoC Peripheral clocks + * + * Copyright (C) 2016 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2 or later. This program is licensed "as is" + * without any warranty of any kind, whether express or implied. + * + * Most of the peripheral clocks can be modelled like this: + * _____ _______ _______ + * TBG-A-P --| | | | | | ______ + * TBG-B-P --| Mux |--| /div1 |--| /div2 |--| Gate |--> perip_clk + * TBG-A-S --| | | | | | |______| + * TBG-B-S --|_____| |_______| |_______| + * + * However some clocks may use only one or two block or and use the + * xtal clock as parent. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define TBG_SEL 0x0 +#define DIV_SEL0 0x4 +#define DIV_SEL1 0x8 +#define DIV_SEL2 0xC +#define CLK_SEL 0x10 +#define CLK_DIS 0x14 + +struct clk_periph_driver_data { + struct clk_hw_onecell_data *hw_data; + spinlock_t lock; +}; + +struct clk_double_div { + struct clk_hw hw; + void __iomem *reg1; + u8 shift1; + void __iomem *reg2; + u8 shift2; +}; + +#define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw) + +struct clk_periph_data { + const char *name; + const char * const *parent_names; + int num_parents; + struct clk_hw *mux_hw; + struct clk_hw *rate_hw; + struct clk_hw *gate_hw; + bool is_double_div; +}; + +static const struct clk_div_table clk_table6[] = { + { .val = 1, .div = 1, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 3, }, + { .val = 4, .div = 4, }, + { .val = 5, .div = 5, }, + { .val = 6, .div = 6, }, + { .val = 0, .div = 0, }, /* last entry */ +}; + +static const struct clk_div_table clk_table1[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 0, }, /* last entry */ +}; + +static const struct clk_div_table clk_table2[] = { + { .val = 0, .div = 2, }, + { .val = 1, .div = 4, }, + { .val = 0, .div = 0, }, /* last entry */ +}; +static const struct clk_ops clk_double_div_ops; + +#define PERIPH_GATE(_name, _bit) \ +struct clk_gate gate_##_name = { \ + .reg = (void *)CLK_DIS, \ + .bit_idx = _bit, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_gate_ops, \ + } \ +}; + +#define PERIPH_MUX(_name, _shift) \ +struct clk_mux mux_##_name = { \ + .reg = (void *)TBG_SEL, \ + .shift = _shift, \ + .mask = 3, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_mux_ro_ops, \ + } \ +}; + +#define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \ +struct clk_double_div rate_##_name = { \ + .reg1 = (void *)_reg1, \ + .reg2 = (void *)_reg2, \ + .shift1 = _shift1, \ + .shift2 = _shift2, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_double_div_ops, \ + } \ +}; + +#define PERIPH_DIV(_name, _reg, _shift, _table) \ +struct clk_divider rate_##_name = { \ + .reg = (void *)_reg, \ + .table = _table, \ + .shift = _shift, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_divider_ro_ops, \ + } \ +}; + +#define PERIPH_CLK_FULL_DD(_name, _bit, _shift, _reg1, _reg2, _shift1, _shift2)\ +static PERIPH_GATE(_name, _bit); \ +static PERIPH_MUX(_name, _shift); \ +static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2); + +#define PERIPH_CLK_FULL(_name, _bit, _shift, _reg, _shift1, _table) \ +static PERIPH_GATE(_name, _bit); \ +static PERIPH_MUX(_name, _shift); \ +static PERIPH_DIV(_name, _reg, _shift1, _table); + +#define PERIPH_CLK_GATE_DIV(_name, _bit, _reg, _shift, _table) \ +static PERIPH_GATE(_name, _bit); \ +static PERIPH_DIV(_name, _reg, _shift, _table); + +#define PERIPH_CLK_MUX_DIV(_name, _shift, _reg, _shift_div, _table) \ +static PERIPH_MUX(_name, _shift); \ +static PERIPH_DIV(_name, _reg, _shift_div, _table); + +#define PERIPH_CLK_MUX_DD(_name, _shift, _reg1, _reg2, _shift1, _shift2)\ +static PERIPH_MUX(_name, _shift); \ +static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2); + +#define REF_CLK_FULL(_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ "TBG-A-P", \ + "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ + .num_parents = 4, \ + .mux_hw = &mux_##_name.hw, \ + .gate_hw = &gate_##_name.hw, \ + .rate_hw = &rate_##_name.hw, \ + } + +#define REF_CLK_FULL_DD(_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ "TBG-A-P", \ + "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ + .num_parents = 4, \ + .mux_hw = &mux_##_name.hw, \ + .gate_hw = &gate_##_name.hw, \ + .rate_hw = &rate_##_name.hw, \ + .is_double_div = true, \ + } + +#define REF_CLK_GATE(_name, _parent_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ _parent_name}, \ + .num_parents = 1, \ + .gate_hw = &gate_##_name.hw, \ + } + +#define REF_CLK_GATE_DIV(_name, _parent_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ _parent_name}, \ + .num_parents = 1, \ + .gate_hw = &gate_##_name.hw, \ + .rate_hw = &rate_##_name.hw, \ + } + +#define REF_CLK_MUX_DIV(_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ "TBG-A-P", \ + "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ + .num_parents = 4, \ + .mux_hw = &mux_##_name.hw, \ + .rate_hw = &rate_##_name.hw, \ + } + +#define REF_CLK_MUX_DD(_name) \ + { .name = #_name, \ + .parent_names = (const char *[]){ "TBG-A-P", \ + "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ + .num_parents = 4, \ + .mux_hw = &mux_##_name.hw, \ + .rate_hw = &rate_##_name.hw, \ + .is_double_div = true, \ + } + +/* NB periph clocks */ +PERIPH_CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13); +PERIPH_CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7); +PERIPH_CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0); +PERIPH_CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6); +PERIPH_CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12); +PERIPH_CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, clk_table6); +static PERIPH_GATE(avs, 11); +PERIPH_CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0); +PERIPH_CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24); +static PERIPH_GATE(i2c_2, 16); +static PERIPH_GATE(i2c_1, 17); +PERIPH_CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, clk_table2); +PERIPH_CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12); +PERIPH_CLK_FULL(trace, 22, 18, DIV_SEL0, 20, clk_table6); +PERIPH_CLK_FULL(counter, 23, 20, DIV_SEL0, 23, clk_table6); +PERIPH_CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19); +PERIPH_CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, clk_table6); + +static struct clk_periph_data data_nb[] ={ + REF_CLK_FULL_DD(mmc), + REF_CLK_FULL_DD(sata_host), + REF_CLK_FULL_DD(sec_at), + REF_CLK_FULL_DD(sec_dap), + REF_CLK_FULL_DD(tscem), + REF_CLK_FULL(tscem_tmx), + REF_CLK_GATE(avs, "xtal"), + REF_CLK_FULL_DD(sqf), + REF_CLK_FULL_DD(pwm), + REF_CLK_GATE(i2c_2, "xtal"), + REF_CLK_GATE(i2c_1, "xtal"), + REF_CLK_GATE_DIV(ddr_phy, "TBG-A-S"), + REF_CLK_FULL_DD(ddr_fclk), + REF_CLK_FULL(trace), + REF_CLK_FULL(counter), + REF_CLK_FULL_DD(eip97), + REF_CLK_MUX_DIV(cpu), + { }, +}; + +/* SB periph clocks */ +PERIPH_CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9); +PERIPH_CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21); +PERIPH_CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9); +static PERIPH_GATE(gbe1_50, 0); +static PERIPH_GATE(gbe0_50, 1); +static PERIPH_GATE(gbe1_125, 2); +static PERIPH_GATE(gbe0_125, 3); +PERIPH_CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, clk_table1); +PERIPH_CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, clk_table1); +PERIPH_CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, clk_table1); +PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6); +PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12); +PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18); + +static struct clk_periph_data data_sb[] = { + REF_CLK_MUX_DD(gbe_50), + REF_CLK_MUX_DD(gbe_core), + REF_CLK_MUX_DD(gbe_125), + REF_CLK_GATE(gbe1_50, "gbe_50"), + REF_CLK_GATE(gbe0_50, "gbe_50"), + REF_CLK_GATE(gbe1_125, "gbe_125"), + REF_CLK_GATE(gbe0_125, "gbe_125"), + REF_CLK_GATE_DIV(gbe1_core, "gbe_core"), + REF_CLK_GATE_DIV(gbe0_core, "gbe_core"), + REF_CLK_GATE_DIV(gbe_bm, "gbe_core"), + REF_CLK_FULL_DD(sdio), + REF_CLK_FULL_DD(usb32_usb2_sys), + REF_CLK_FULL_DD(usb32_ss_sys), + { }, +}; + +static unsigned int get_div(void __iomem *reg, int shift) +{ + u32 val; + + val = (readl(reg) >> shift) & 0x7; + if (val > 6) + return 0; + return val; +} + +static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_double_div *double_div = to_clk_double_div(hw); + unsigned int div; + + div = get_div(double_div->reg1, double_div->shift1); + div *= get_div(double_div->reg2, double_div->shift2); + + return DIV_ROUND_UP_ULL((u64)parent_rate, div); +} + +static const struct clk_ops clk_double_div_ops = { + .recalc_rate = clk_double_div_recalc_rate, +}; + +static const struct of_device_id armada_3700_periph_clock_of_match[] = { + { .compatible = "marvell,armada-3700-periph-clock-nb", + .data = data_nb, }, + { .compatible = "marvell,armada-3700-periph-clock-sb", + .data = data_sb, }, + { } +}; +static int armada_3700_add_composite_clk(const struct clk_periph_data *data, + void __iomem *reg, spinlock_t *lock, + struct device *dev, struct clk_hw *hw) +{ + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, + *rate_ops = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL; + + if (data->mux_hw) { + struct clk_mux *mux; + + mux_hw = data->mux_hw; + mux = to_clk_mux(mux_hw); + mux->lock = lock; + mux_ops = mux_hw->init->ops; + mux->reg = reg + (u64)mux->reg; + } + + if (data->gate_hw) { + struct clk_gate *gate; + + gate_hw = data->gate_hw; + gate = to_clk_gate(gate_hw); + gate->lock = lock; + gate_ops = gate_hw->init->ops; + gate->reg = reg + (u64)gate->reg; + } + + if (data->rate_hw) { + rate_hw = data->rate_hw; + rate_ops = rate_hw->init->ops; + if (data->is_double_div) { + struct clk_double_div *rate; + + rate = to_clk_double_div(rate_hw); + rate->reg1 = reg + (u64)rate->reg1; + rate->reg2 = reg + (u64)rate->reg2; + } else { + struct clk_divider *rate = to_clk_divider(rate_hw); + const struct clk_div_table *clkt; + int table_size = 0; + + rate->reg = reg + (u64)rate->reg; + for (clkt = rate->table; clkt->div; clkt++) + table_size++; + rate->width = order_base_2(table_size); + rate->lock = lock; + } + } + + hw = clk_hw_register_composite(dev, data->name, data->parent_names, + data->num_parents, mux_hw, + mux_ops, rate_hw, rate_ops, + gate_hw, gate_ops, CLK_IGNORE_UNUSED); + + if (IS_ERR(hw)) + return PTR_ERR(hw); + + return 0; +} + +static int armada_3700_periph_clock_probe(struct platform_device *pdev) +{ + struct clk_periph_driver_data *driver_data; + struct device_node *np = pdev->dev.of_node; + const struct clk_periph_data *data; + struct device *dev = &pdev->dev; + int num_periph = 0, i, ret; + struct resource *res; + void __iomem *reg; + + data = of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + while (data[num_periph].name) + num_periph++; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL); + if (!driver_data) + return -ENOMEM; + + driver_data->hw_data = devm_kzalloc(dev, sizeof(*driver_data->hw_data) + + sizeof(*driver_data->hw_data->hws) * num_periph, + GFP_KERNEL); + if (!driver_data->hw_data) + return -ENOMEM; + driver_data->hw_data->num = num_periph; + + spin_lock_init(&driver_data->lock); + + for (i = 0; i < num_periph; i++) { + struct clk_hw *hw = driver_data->hw_data->hws[i]; + + if (armada_3700_add_composite_clk(&data[i], reg, + &driver_data->lock, dev, hw)) + dev_err(dev, "Can't register periph clock %s\n", + data[i].name); + + } + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + driver_data->hw_data); + if (ret) { + for (i = 0; i < num_periph; i++) + clk_hw_unregister(driver_data->hw_data->hws[i]); + return ret; + } + + platform_set_drvdata(pdev, driver_data); + return 0; +} + +static int armada_3700_periph_clock_remove(struct platform_device *pdev) +{ + struct clk_periph_driver_data *data = platform_get_drvdata(pdev); + struct clk_hw_onecell_data *hw_data = data->hw_data; + int i; + + of_clk_del_provider(pdev->dev.of_node); + + for (i = 0; i < hw_data->num; i++) + clk_hw_unregister(hw_data->hws[i]); + + return 0; +} + +static struct platform_driver armada_3700_periph_clock_driver = { + .probe = armada_3700_periph_clock_probe, + .remove = armada_3700_periph_clock_remove, + .driver = { + .name = "marvell-armada-3700-periph-clock", + .of_match_table = armada_3700_periph_clock_of_match, + }, +}; + +builtin_platform_driver(armada_3700_periph_clock_driver); diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c new file mode 100644 index 0000000..aa80db1 --- /dev/null +++ b/drivers/clk/mvebu/armada-37xx-tbg.c @@ -0,0 +1,158 @@ +/* + * Marvell Armada 37xx SoC Time Base Generator clocks + * + * Copyright (C) 2016 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2 or later. This program is licensed "as is" + * without any warranty of any kind, whether express or implied. + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define NUM_TBG 4 + +#define TBG_CTRL0 0x4 +#define TBG_CTRL1 0x8 +#define TBG_CTRL7 0x20 +#define TBG_CTRL8 0x30 + +#define TBG_DIV_MASK 0x1FF + +#define TBG_A_REFDIV 0 +#define TBG_B_REFDIV 16 + +#define TBG_A_FBDIV 2 +#define TBG_B_FBDIV 18 + +#define TBG_A_VCODIV_SE 0 +#define TBG_B_VCODIV_SE 16 + +#define TBG_A_VCODIV_DIFF 1 +#define TBG_B_VCODIV_DIFF 17 + +struct tbg_def { + char *name; + u32 refdiv_offset; + u32 fbdiv_offset; + u32 vcodiv_reg; + u32 vcodiv_offset; +}; + +static const struct tbg_def tbg[NUM_TBG] = { + {"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF}, + {"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF}, + {"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE}, + {"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE}, +}; + +static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg) +{ + u32 val; + + val = readl(reg + TBG_CTRL0); + + return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2; +} + +static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg) +{ + u32 val; + unsigned int div; + + val = readl(reg + TBG_CTRL7); + + div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK; + if (div == 0) + div = 1; + val = readl(reg + ptbg->vcodiv_reg); + + div *= 1 << ((val >> ptbg->vcodiv_offset) & TBG_DIV_MASK); + + return div; +} + + +static int armada_3700_tbg_clock_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct clk_hw_onecell_data *hw_tbg_data; + struct device *dev = &pdev->dev; + const char *parent_name; + struct resource *res; + struct clk *parent; + void __iomem *reg; + int i, ret; + + hw_tbg_data = devm_kzalloc(&pdev->dev, sizeof(*hw_tbg_data) + + sizeof(*hw_tbg_data->hws) * NUM_TBG, + GFP_KERNEL); + if (!hw_tbg_data) + return -ENOMEM; + hw_tbg_data->num = NUM_TBG; + platform_set_drvdata(pdev, hw_tbg_data); + + parent = devm_clk_get(dev, NULL); + if (IS_ERR(parent)) { + dev_err(dev, "Could get the clock parent\n"); + return -EINVAL; + } + parent_name = __clk_get_name(parent); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + for (i = 0; i < NUM_TBG; i++) { + const char *name; + unsigned int mult, div; + + name = tbg[i].name; + mult = tbg_get_mult(reg, &tbg[i]); + div = tbg_get_div(reg, &tbg[i]); + hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name, + parent_name, 0, mult, div); + if (IS_ERR(hw_tbg_data->hws[i])) + dev_err(dev, "Can't register TBG clock %s\n", name); + } + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data); + + return ret; +} + +static int armada_3700_tbg_clock_remove(struct platform_device *pdev) +{ + int i; + struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + for (i = 0; i < hw_tbg_data->num; i++) + clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]); + + return 0; +} + +static const struct of_device_id armada_3700_tbg_clock_of_match[] = { + { .compatible = "marvell,armada-3700-tbg-clock", }, + { } +}; + +static struct platform_driver armada_3700_tbg_clock_driver = { + .probe = armada_3700_tbg_clock_probe, + .remove = armada_3700_tbg_clock_remove, + .driver = { + .name = "marvell-armada-3700-tbg-clock", + .of_match_table = armada_3700_tbg_clock_of_match, + }, +}; + +builtin_platform_driver(armada_3700_tbg_clock_driver); diff --git a/drivers/clk/mvebu/armada-37xx-xtal.c b/drivers/clk/mvebu/armada-37xx-xtal.c new file mode 100644 index 0000000..612d65e --- /dev/null +++ b/drivers/clk/mvebu/armada-37xx-xtal.c @@ -0,0 +1,91 @@ +/* + * Marvell Armada 37xx SoC xtal clocks + * + * Copyright (C) 2016 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define NB_GPIO1_LATCH 0xC +#define XTAL_MODE BIT(31) + +static int armada_3700_xtal_clock_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const char *xtal_name = "xtal"; + struct device_node *parent; + struct regmap *regmap; + struct clk_hw *xtal_hw; + unsigned int rate; + u32 reg; + int ret; + + xtal_hw = devm_kzalloc(&pdev->dev, sizeof(*xtal_hw), GFP_KERNEL); + if (!xtal_hw) + return -ENOMEM; + + platform_set_drvdata(pdev, xtal_hw); + + parent = np->parent; + if (!parent) { + dev_err(&pdev->dev, "no parent\n"); + return -ENODEV; + } + + regmap = syscon_node_to_regmap(parent); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "cannot get regmap\n"); + return PTR_ERR(regmap); + } + + ret = regmap_read(regmap, NB_GPIO1_LATCH, ®); + if (ret) { + dev_err(&pdev->dev, "cannot read from regmap\n"); + return ret; + } + + if (reg & XTAL_MODE) + rate = 40000000; + else + rate = 25000000; + + of_property_read_string_index(np, "clock-output-names", 0, &xtal_name); + xtal_hw = clk_hw_register_fixed_rate(NULL, xtal_name, NULL, 0, rate); + if (IS_ERR(xtal_hw)) + return PTR_ERR(xtal_hw); + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, xtal_hw); + + return ret; +} + +static int armada_3700_xtal_clock_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id armada_3700_xtal_clock_of_match[] = { + { .compatible = "marvell,armada-3700-xtal-clock", }, + { } +}; + +static struct platform_driver armada_3700_xtal_clock_driver = { + .probe = armada_3700_xtal_clock_probe, + .remove = armada_3700_xtal_clock_remove, + .driver = { + .name = "marvell-armada-3700-xtal-clock", + .of_match_table = armada_3700_xtal_clock_of_match, + }, +}; + +builtin_platform_driver(armada_3700_xtal_clock_driver); diff --git a/drivers/clk/mvebu/armada-39x.c b/drivers/clk/mvebu/armada-39x.c index efb974d..4fdfd32 100644 --- a/drivers/clk/mvebu/armada-39x.c +++ b/drivers/clk/mvebu/armada-39x.c @@ -142,6 +142,8 @@ static const struct clk_gating_soc_desc armada_39x_gating_desc[] __initconst = { { "pex3", NULL, 7 }, { "pex0", NULL, 8 }, { "usb3h0", NULL, 9 }, + { "usb3h1", NULL, 10 }, + { "sata0", NULL, 15 }, { "sdio", NULL, 17 }, { "xor0", NULL, 22 }, { "xor1", NULL, 28 }, diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 7fa42d6..f2303da 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -81,13 +81,6 @@ enum { #define CP110_GATE_EIP150 25 #define CP110_GATE_EIP197 26 -static struct clk *cp110_clks[CP110_CLK_NUM]; - -static struct clk_onecell_data cp110_clk_data = { - .clks = cp110_clks, - .clk_num = CP110_CLK_NUM, -}; - struct cp110_gate_clk { struct clk_hw hw; struct regmap *regmap; @@ -142,6 +135,8 @@ static struct clk *cp110_register_gate(const char *name, if (!gate) return ERR_PTR(-ENOMEM); + memset(&init, 0, sizeof(init)); + init.name = name; init.ops = &cp110_gate_ops; init.parent_names = &parent_name; @@ -194,7 +189,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) struct regmap *regmap; struct device_node *np = pdev->dev.of_node; const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; - struct clk *clk; + struct clk_onecell_data *cp110_clk_data; + struct clk *clk, **cp110_clks; u32 nand_clk_ctrl; int i, ret; @@ -207,6 +203,20 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) if (ret) return ret; + cp110_clks = devm_kcalloc(&pdev->dev, sizeof(struct clk *), + CP110_CLK_NUM, GFP_KERNEL); + if (!cp110_clks) + return -ENOMEM; + + cp110_clk_data = devm_kzalloc(&pdev->dev, + sizeof(*cp110_clk_data), + GFP_KERNEL); + if (!cp110_clk_data) + return -ENOMEM; + + cp110_clk_data->clks = cp110_clks; + cp110_clk_data->clk_num = CP110_CLK_NUM; + /* Register the APLL which is the root of the clk tree */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_APLL, &apll_name); @@ -334,10 +344,12 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk; } - ret = of_clk_add_provider(np, cp110_of_clk_get, &cp110_clk_data); + ret = of_clk_add_provider(np, cp110_of_clk_get, cp110_clk_data); if (ret) goto fail_clk_add; + platform_set_drvdata(pdev, cp110_clks); + return 0; fail_clk_add: @@ -364,6 +376,7 @@ fail0: static int cp110_syscon_clk_remove(struct platform_device *pdev) { + struct clk **cp110_clks = platform_get_drvdata(pdev); int i; of_clk_del_provider(pdev->dev.of_node); diff --git a/drivers/clk/nxp/clk-lpc18xx-creg.c b/drivers/clk/nxp/clk-lpc18xx-creg.c index 9e35749..c6e802e 100644 --- a/drivers/clk/nxp/clk-lpc18xx-creg.c +++ b/drivers/clk/nxp/clk-lpc18xx-creg.c @@ -184,7 +184,8 @@ static void __init lpc18xx_creg_clk_init(struct device_node *np) of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data); } -CLK_OF_DECLARE(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk", lpc18xx_creg_clk_init); +CLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk", + lpc18xx_creg_clk_init); static struct clk *clk_creg[CREG_CLK_MAX]; static struct clk_onecell_data clk_creg_data = { diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index 90d740a..34c9735 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -1513,6 +1513,7 @@ static void __init lpc32xx_clk_init(struct device_node *np) if (IS_ERR(clk_regmap)) { pr_err("failed to regmap system control block: %ld\n", PTR_ERR(clk_regmap)); + iounmap(base); return; } diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 95e3b3e..0146d3c 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -87,6 +87,23 @@ config MSM_LCC_8960 Say Y if you want to use audio devices such as i2s, pcm, SLIMBus, etc. +config MDM_GCC_9615 + tristate "MDM9615 Global Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on mdm9615 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, etc. + +config MDM_LCC_9615 + tristate "MDM9615 LPASS Clock Controller" + select MDM_GCC_9615 + depends on COMMON_CLK_QCOM + help + Support for the LPASS clock controller on mdm9615 devices. + Say Y if you want to use audio devices such as i2s, pcm, + SLIMBus, etc. + config MSM_MMCC_8960 tristate "MSM8960 Multimedia Clock Controller" select MSM_GCC_8960 @@ -117,6 +134,7 @@ config MSM_MMCC_8974 config MSM_GCC_8996 tristate "MSM8996 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on msm8996 devices. @@ -126,6 +144,7 @@ config MSM_GCC_8996 config MSM_MMCC_8996 tristate "MSM8996 Multimedia Clock Controller" select MSM_GCC_8996 + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the multimedia clock controller on msm8996 devices. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 2a25f4e..1fb1f54 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,17 +12,20 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-y += reset.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +# Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o +obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o +obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o -obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o +obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index a58ba39..1c856d3 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -101,14 +101,13 @@ EXPORT_SYMBOL_GPL(clk_disable_regmap); * clk_regmap struct via this function so that the regmap is initialized * and so that the clock is registered with the common clock framework. */ -struct clk *devm_clk_register_regmap(struct device *dev, - struct clk_regmap *rclk) +int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) { if (dev && dev_get_regmap(dev, NULL)) rclk->regmap = dev_get_regmap(dev, NULL); else if (dev && dev->parent) rclk->regmap = dev_get_regmap(dev->parent, NULL); - return devm_clk_register(dev, &rclk->hw); + return devm_clk_hw_register(dev, &rclk->hw); } EXPORT_SYMBOL_GPL(devm_clk_register_regmap); diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 491a63d..90d95cd 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -39,7 +39,6 @@ struct clk_regmap { int clk_is_enabled_regmap(struct clk_hw *hw); int clk_enable_regmap(struct clk_hw *hw); void clk_disable_regmap(struct clk_hw *hw); -struct clk * -devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk); +int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk); #endif diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index f7c226a..fffcbaf 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -27,8 +27,8 @@ struct qcom_cc { struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; + struct clk_regmap **rclks; + size_t num_rclks; }; const @@ -102,8 +102,8 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, struct device_node *clocks_node; struct clk_fixed_factor *factor; struct clk_fixed_rate *fixed; - struct clk *clk; struct clk_init_data init_data = { }; + int ret; clocks_node = of_find_node_by_path("/clocks"); if (clocks_node) @@ -121,9 +121,9 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, init_data.name = path; init_data.ops = &clk_fixed_rate_ops; - clk = devm_clk_register(dev, &fixed->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(dev, &fixed->hw); + if (ret) + return ret; } of_node_put(node); @@ -141,9 +141,9 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, init_data.flags = 0; init_data.ops = &clk_fixed_factor_ops; - clk = devm_clk_register(dev, &factor->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(dev, &factor->hw); + if (ret) + return ret; } return 0; @@ -174,42 +174,48 @@ int qcom_cc_register_sleep_clk(struct device *dev) } EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk); +static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct qcom_cc *cc = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= cc->num_rclks) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return cc->rclks[idx] ? &cc->rclks[idx]->hw : ERR_PTR(-ENOENT); +} + int qcom_cc_really_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc, struct regmap *regmap) { int i, ret; struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; struct qcom_reset_controller *reset; struct qcom_cc *cc; struct gdsc_desc *scd; size_t num_clks = desc->num_clks; struct clk_regmap **rclks = desc->clks; - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); + cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); if (!cc) return -ENOMEM; - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; + cc->rclks = rclks; + cc->num_rclks = num_clks; for (i = 0; i < num_clks; i++) { - if (!rclks[i]) { - clks[i] = ERR_PTR(-ENOENT); + if (!rclks[i]) continue; - } - clk = devm_clk_register_regmap(dev, rclks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; + + ret = devm_clk_register_regmap(dev, rclks[i]); + if (ret) + return ret; } - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); + ret = of_clk_add_hw_provider(dev->of_node, qcom_cc_clk_hw_get, cc); if (ret) return ret; diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index 3cd1af0..b593065 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -1332,7 +1332,6 @@ static struct platform_driver gcc_ipq4019_driver = { .probe = gcc_ipq4019_probe, .driver = { .name = "qcom,gcc-ipq4019", - .owner = THIS_MODULE, .of_match_table = gcc_ipq4019_match_table, }, }; diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c new file mode 100644 index 0000000..581a17f --- /dev/null +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -0,0 +1,1727 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) BayLibre, SAS. + * Author : Neil Armstrong <narmstrong@baylibre.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/clock/qcom,gcc-mdm9615.h> +#include <dt-bindings/reset/qcom,gcc-mdm9615.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" + +static struct clk_fixed_factor cxo = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "cxo", + .parent_names = (const char *[]){ "cxo_board" }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_pll pll0 = { + .l_reg = 0x30c4, + .m_reg = 0x30c8, + .n_reg = 0x30cc, + .config_reg = 0x30d4, + .mode_reg = 0x30c0, + .status_reg = 0x30d8, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll0", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll0_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "pll0_vote", + .parent_names = (const char *[]){ "pll8" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_regmap pll4_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pll4_vote", + .parent_names = (const char *[]){ "pll4" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll pll8 = { + .l_reg = 0x3144, + .m_reg = 0x3148, + .n_reg = 0x314c, + .config_reg = 0x3154, + .mode_reg = 0x3140, + .status_reg = 0x3158, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll8", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll8_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "pll8_vote", + .parent_names = (const char *[]){ "pll8" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, + .n_reg = 0x31cc, + .config_reg = 0x31d4, + .mode_reg = 0x31c0, + .status_reg = 0x31d8, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll14", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll14_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "pll14_vote", + .parent_names = (const char *[]){ "pll14" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +enum { + P_CXO, + P_PLL8, + P_PLL14, +}; + +static const struct parent_map gcc_cxo_pll8_map[] = { + { P_CXO, 0 }, + { P_PLL8, 3 } +}; + +static const char * const gcc_cxo_pll8[] = { + "cxo", + "pll8_vote", +}; + +static const struct parent_map gcc_cxo_pll14_map[] = { + { P_CXO, 0 }, + { P_PLL14, 4 } +}; + +static const char * const gcc_cxo_pll14[] = { + "cxo", + "pll14_vote", +}; + +static const struct parent_map gcc_cxo_map[] = { + { P_CXO, 0 }, +}; + +static const char * const gcc_cxo[] = { + "cxo", +}; + +static struct freq_tbl clk_tbl_gsbi_uart[] = { + { 1843200, P_PLL8, 2, 6, 625 }, + { 3686400, P_PLL8, 2, 12, 625 }, + { 7372800, P_PLL8, 2, 24, 625 }, + { 14745600, P_PLL8, 2, 48, 625 }, + { 16000000, P_PLL8, 4, 1, 6 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 32000000, P_PLL8, 4, 1, 3 }, + { 40000000, P_PLL8, 1, 5, 48 }, + { 46400000, P_PLL8, 1, 29, 240 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 51200000, P_PLL8, 1, 2, 15 }, + { 56000000, P_PLL8, 1, 7, 48 }, + { 58982400, P_PLL8, 1, 96, 625 }, + { 64000000, P_PLL8, 2, 1, 3 }, + { } +}; + +static struct clk_rcg gsbi1_uart_src = { + .ns_reg = 0x29d4, + .md_reg = 0x29d0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x29d4, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_uart_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi1_uart_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x29d4, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_uart_clk", + .parent_names = (const char *[]){ + "gsbi1_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi2_uart_src = { + .ns_reg = 0x29f4, + .md_reg = 0x29f0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x29f4, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_uart_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi2_uart_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x29f4, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_uart_clk", + .parent_names = (const char *[]){ + "gsbi2_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi3_uart_src = { + .ns_reg = 0x2a14, + .md_reg = 0x2a10, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a14, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi3_uart_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi3_uart_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 2, + .clkr = { + .enable_reg = 0x2a14, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi3_uart_clk", + .parent_names = (const char *[]){ + "gsbi3_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi4_uart_src = { + .ns_reg = 0x2a34, + .md_reg = 0x2a30, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a34, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_uart_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi4_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 26, + .clkr = { + .enable_reg = 0x2a34, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_uart_clk", + .parent_names = (const char *[]){ + "gsbi4_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi5_uart_src = { + .ns_reg = 0x2a54, + .md_reg = 0x2a50, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a54, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_uart_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi5_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 22, + .clkr = { + .enable_reg = 0x2a54, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_uart_clk", + .parent_names = (const char *[]){ + "gsbi5_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct freq_tbl clk_tbl_gsbi_qup[] = { + { 960000, P_CXO, 4, 1, 5 }, + { 4800000, P_CXO, 4, 0, 1 }, + { 9600000, P_CXO, 2, 0, 1 }, + { 15060000, P_PLL8, 1, 2, 51 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 25600000, P_PLL8, 1, 1, 15 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 51200000, P_PLL8, 1, 2, 15 }, + { } +}; + +static struct clk_rcg gsbi1_qup_src = { + .ns_reg = 0x29cc, + .md_reg = 0x29c8, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x29cc, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_qup_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi1_qup_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x29cc, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_qup_clk", + .parent_names = (const char *[]){ "gsbi1_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi2_qup_src = { + .ns_reg = 0x29ec, + .md_reg = 0x29e8, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x29ec, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_qup_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi2_qup_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 4, + .clkr = { + .enable_reg = 0x29ec, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_qup_clk", + .parent_names = (const char *[]){ "gsbi2_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi3_qup_src = { + .ns_reg = 0x2a0c, + .md_reg = 0x2a08, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a0c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi3_qup_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi3_qup_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 0, + .clkr = { + .enable_reg = 0x2a0c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi3_qup_clk", + .parent_names = (const char *[]){ "gsbi3_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi4_qup_src = { + .ns_reg = 0x2a2c, + .md_reg = 0x2a28, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a2c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_qup_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi4_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x2a2c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_qup_clk", + .parent_names = (const char *[]){ "gsbi4_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi5_qup_src = { + .ns_reg = 0x2a4c, + .md_reg = 0x2a48, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a4c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_qup_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi5_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x2a4c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_qup_clk", + .parent_names = (const char *[]){ "gsbi5_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_gp[] = { + { 9600000, P_CXO, 2, 0, 0 }, + { 19200000, P_CXO, 1, 0, 0 }, + { } +}; + +static struct clk_rcg gp0_src = { + .ns_reg = 0x2d24, + .md_reg = 0x2d00, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d24, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp0_src", + .parent_names = gcc_cxo, + .num_parents = 1, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + } +}; + +static struct clk_branch gp0_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x2d24, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp0_clk", + .parent_names = (const char *[]){ "gp0_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gp1_src = { + .ns_reg = 0x2d44, + .md_reg = 0x2d40, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d44, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp1_src", + .parent_names = gcc_cxo, + .num_parents = 1, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch gp1_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x2d44, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp1_clk", + .parent_names = (const char *[]){ "gp1_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gp2_src = { + .ns_reg = 0x2d64, + .md_reg = 0x2d60, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d64, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp2_src", + .parent_names = gcc_cxo, + .num_parents = 1, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch gp2_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 5, + .clkr = { + .enable_reg = 0x2d64, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp2_clk", + .parent_names = (const char *[]){ "gp2_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch pmem_clk = { + .hwcg_reg = 0x25a0, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x25a0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pmem_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_rcg prng_src = { + .ns_reg = 0x2e80, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "prng_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch prng_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "prng_clk", + .parent_names = (const char *[]){ "prng_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + }, + }, +}; + +static const struct freq_tbl clk_tbl_sdc[] = { + { 144000, P_CXO, 1, 1, 133 }, + { 400000, P_PLL8, 4, 1, 240 }, + { 16000000, P_PLL8, 4, 1, 6 }, + { 17070000, P_PLL8, 1, 2, 45 }, + { 20210000, P_PLL8, 1, 1, 19 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 38400000, P_PLL8, 2, 1, 5 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 64000000, P_PLL8, 3, 1, 2 }, + { 76800000, P_PLL8, 1, 1, 5 }, + { } +}; + +static struct clk_rcg sdc1_src = { + .ns_reg = 0x282c, + .md_reg = 0x2828, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_sdc, + .clkr = { + .enable_reg = 0x282c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch sdc1_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x282c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_clk", + .parent_names = (const char *[]){ "sdc1_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg sdc2_src = { + .ns_reg = 0x284c, + .md_reg = 0x2848, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_sdc, + .clkr = { + .enable_reg = 0x284c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "sdc2_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch sdc2_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 5, + .clkr = { + .enable_reg = 0x284c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "sdc2_clk", + .parent_names = (const char *[]){ "sdc2_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb[] = { + { 60000000, P_PLL8, 1, 5, 32 }, + { } +}; + +static struct clk_rcg usb_hs1_xcvr_src = { + .ns_reg = 0x290c, + .md_reg = 0x2908, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x290c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_xcvr_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hs1_xcvr_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 0, + .clkr = { + .enable_reg = 0x290c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_xcvr_clk", + .parent_names = (const char *[]){ "usb_hs1_xcvr_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg usb_hsic_xcvr_fs_src = { + .ns_reg = 0x2928, + .md_reg = 0x2924, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x2928, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hsic_xcvr_fs_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hsic_xcvr_fs_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x2928, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_hsic_xcvr_fs_clk", + .parent_names = + (const char *[]){ "usb_hsic_xcvr_fs_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb_hs1_system[] = { + { 60000000, P_PLL8, 1, 5, 32 }, + { } +}; + +static struct clk_rcg usb_hs1_system_src = { + .ns_reg = 0x36a4, + .md_reg = 0x36a0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb_hs1_system, + .clkr = { + .enable_reg = 0x36a4, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_system_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hs1_system_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 4, + .clkr = { + .enable_reg = 0x36a4, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .parent_names = + (const char *[]){ "usb_hs1_system_src" }, + .num_parents = 1, + .name = "usb_hs1_system_clk", + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb_hsic_system[] = { + { 64000000, P_PLL8, 1, 1, 6 }, + { } +}; + +static struct clk_rcg usb_hsic_system_src = { + .ns_reg = 0x2b58, + .md_reg = 0x2b54, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb_hsic_system, + .clkr = { + .enable_reg = 0x2b58, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hsic_system_src", + .parent_names = gcc_cxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hsic_system_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x2b58, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .parent_names = + (const char *[]){ "usb_hsic_system_src" }, + .num_parents = 1, + .name = "usb_hsic_system_clk", + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb_hsic_hsic[] = { + { 48000000, P_PLL14, 1, 0, 0 }, + { } +}; + +static struct clk_rcg usb_hsic_hsic_src = { + .ns_reg = 0x2b50, + .md_reg = 0x2b4c, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_cxo_pll14_map, + }, + .freq_tbl = clk_tbl_usb_hsic_hsic, + .clkr = { + .enable_reg = 0x2b50, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hsic_hsic_src", + .parent_names = gcc_cxo_pll14, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hsic_hsic_clk = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2b50, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "usb_hsic_hsic_src" }, + .num_parents = 1, + .name = "usb_hsic_hsic_clk", + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb_hsic_hsio_cal_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 8, + .clkr = { + .enable_reg = 0x2b48, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .name = "usb_hsic_hsio_cal_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ce1_core_clk = { + .hwcg_reg = 0x2724, + .hwcg_bit = 6, + .halt_reg = 0x2fd4, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x2724, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ce1_core_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ce1_h_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x2720, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ce1_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch dma_bam_h_clk = { + .hwcg_reg = 0x25c0, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x25c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "dma_bam_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gsbi1_h_clk = { + .hwcg_reg = 0x29c0, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 11, + .clkr = { + .enable_reg = 0x29c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gsbi2_h_clk = { + .hwcg_reg = 0x29e0, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x29e0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gsbi3_h_clk = { + .hwcg_reg = 0x2a00, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 3, + .clkr = { + .enable_reg = 0x2a00, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi3_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gsbi4_h_clk = { + .hwcg_reg = 0x2a20, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x2a20, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gsbi5_h_clk = { + .hwcg_reg = 0x2a40, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x2a40, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch usb_hs1_h_clk = { + .hwcg_reg = 0x2900, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x2900, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch usb_hsic_h_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 28, + .clkr = { + .enable_reg = 0x2920, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_hsic_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch sdc1_h_clk = { + .hwcg_reg = 0x2820, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 11, + .clkr = { + .enable_reg = 0x2820, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch sdc2_h_clk = { + .hwcg_reg = 0x2840, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x2840, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sdc2_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch adm0_clk = { + .halt_reg = 0x2fdc, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 14, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "adm0_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch adm0_pbus_clk = { + .hwcg_reg = 0x2208, + .hwcg_bit = 6, + .halt_reg = 0x2fdc, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 13, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(3), + .hw.init = &(struct clk_init_data){ + .name = "adm0_pbus_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch pmic_arb0_h_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 22, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "pmic_arb0_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch pmic_arb1_h_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 21, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pmic_arb1_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch pmic_ssbi2_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "pmic_ssbi2_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch rpm_msg_ram_h_clk = { + .hwcg_reg = 0x27e0, + .hwcg_bit = 6, + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "rpm_msg_ram_h_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_hw *gcc_mdm9615_hws[] = { + &cxo.hw, +}; + +static struct clk_regmap *gcc_mdm9615_clks[] = { + [PLL0] = &pll0.clkr, + [PLL0_VOTE] = &pll0_vote, + [PLL4_VOTE] = &pll4_vote, + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, + [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, + [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, + [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, + [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr, + [GSBI3_UART_SRC] = &gsbi3_uart_src.clkr, + [GSBI3_UART_CLK] = &gsbi3_uart_clk.clkr, + [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr, + [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr, + [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr, + [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr, + [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr, + [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr, + [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr, + [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr, + [GSBI3_QUP_SRC] = &gsbi3_qup_src.clkr, + [GSBI3_QUP_CLK] = &gsbi3_qup_clk.clkr, + [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr, + [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr, + [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr, + [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr, + [GP0_SRC] = &gp0_src.clkr, + [GP0_CLK] = &gp0_clk.clkr, + [GP1_SRC] = &gp1_src.clkr, + [GP1_CLK] = &gp1_clk.clkr, + [GP2_SRC] = &gp2_src.clkr, + [GP2_CLK] = &gp2_clk.clkr, + [PMEM_A_CLK] = &pmem_clk.clkr, + [PRNG_SRC] = &prng_src.clkr, + [PRNG_CLK] = &prng_clk.clkr, + [SDC1_SRC] = &sdc1_src.clkr, + [SDC1_CLK] = &sdc1_clk.clkr, + [SDC2_SRC] = &sdc2_src.clkr, + [SDC2_CLK] = &sdc2_clk.clkr, + [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_src.clkr, + [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr, + [USB_HS1_SYSTEM_CLK_SRC] = &usb_hs1_system_src.clkr, + [USB_HS1_SYSTEM_CLK] = &usb_hs1_system_clk.clkr, + [USB_HSIC_XCVR_FS_SRC] = &usb_hsic_xcvr_fs_src.clkr, + [USB_HSIC_XCVR_FS_CLK] = &usb_hsic_xcvr_fs_clk.clkr, + [USB_HSIC_SYSTEM_CLK_SRC] = &usb_hsic_system_src.clkr, + [USB_HSIC_SYSTEM_CLK] = &usb_hsic_system_clk.clkr, + [USB_HSIC_HSIC_CLK_SRC] = &usb_hsic_hsic_src.clkr, + [USB_HSIC_HSIC_CLK] = &usb_hsic_hsic_clk.clkr, + [USB_HSIC_HSIO_CAL_CLK] = &usb_hsic_hsio_cal_clk.clkr, + [CE1_CORE_CLK] = &ce1_core_clk.clkr, + [CE1_H_CLK] = &ce1_h_clk.clkr, + [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr, + [GSBI1_H_CLK] = &gsbi1_h_clk.clkr, + [GSBI2_H_CLK] = &gsbi2_h_clk.clkr, + [GSBI3_H_CLK] = &gsbi3_h_clk.clkr, + [GSBI4_H_CLK] = &gsbi4_h_clk.clkr, + [GSBI5_H_CLK] = &gsbi5_h_clk.clkr, + [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr, + [USB_HSIC_H_CLK] = &usb_hsic_h_clk.clkr, + [SDC1_H_CLK] = &sdc1_h_clk.clkr, + [SDC2_H_CLK] = &sdc2_h_clk.clkr, + [ADM0_CLK] = &adm0_clk.clkr, + [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr, + [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr, + [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, + [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, + [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, +}; + +static const struct qcom_reset_map gcc_mdm9615_resets[] = { + [DMA_BAM_RESET] = { 0x25c0, 7 }, + [CE1_H_RESET] = { 0x2720, 7 }, + [CE1_CORE_RESET] = { 0x2724, 7 }, + [SDC1_RESET] = { 0x2830 }, + [SDC2_RESET] = { 0x2850 }, + [ADM0_C2_RESET] = { 0x220c, 4 }, + [ADM0_C1_RESET] = { 0x220c, 3 }, + [ADM0_C0_RESET] = { 0x220c, 2 }, + [ADM0_PBUS_RESET] = { 0x220c, 1 }, + [ADM0_RESET] = { 0x220c }, + [USB_HS1_RESET] = { 0x2910 }, + [USB_HSIC_RESET] = { 0x2934 }, + [GSBI1_RESET] = { 0x29dc }, + [GSBI2_RESET] = { 0x29fc }, + [GSBI3_RESET] = { 0x2a1c }, + [GSBI4_RESET] = { 0x2a3c }, + [GSBI5_RESET] = { 0x2a5c }, + [PDM_RESET] = { 0x2CC0, 12 }, +}; + +static const struct regmap_config gcc_mdm9615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3660, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_mdm9615_desc = { + .config = &gcc_mdm9615_regmap_config, + .clks = gcc_mdm9615_clks, + .num_clks = ARRAY_SIZE(gcc_mdm9615_clks), + .resets = gcc_mdm9615_resets, + .num_resets = ARRAY_SIZE(gcc_mdm9615_resets), +}; + +static const struct of_device_id gcc_mdm9615_match_table[] = { + { .compatible = "qcom,gcc-mdm9615" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_mdm9615_match_table); + +static int gcc_mdm9615_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + int ret; + int i; + + regmap = qcom_cc_map(pdev, &gcc_mdm9615_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + for (i = 0; i < ARRAY_SIZE(gcc_mdm9615_hws); i++) { + ret = devm_clk_hw_register(dev, gcc_mdm9615_hws[i]); + if (ret) + return ret; + } + + return qcom_cc_really_probe(pdev, &gcc_mdm9615_desc, regmap); +} + +static struct platform_driver gcc_mdm9615_driver = { + .probe = gcc_mdm9615_probe, + .driver = { + .name = "gcc-mdm9615", + .of_match_table = gcc_mdm9615_match_table, + }, +}; + +static int __init gcc_mdm9615_init(void) +{ + return platform_driver_register(&gcc_mdm9615_driver); +} +core_initcall(gcc_mdm9615_init); + +static void __exit gcc_mdm9615_exit(void) +{ + platform_driver_unregister(&gcc_mdm9615_driver); +} +module_exit(gcc_mdm9615_exit); + +MODULE_DESCRIPTION("QCOM GCC MDM9615 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gcc-mdm9615"); diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index bbf732b..fe03e6f 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -2592,9 +2592,9 @@ static struct clk_branch gcc_pcie_2_aux_clk = { }; static struct clk_branch gcc_pcie_2_pipe_clk = { - .halt_reg = 0x6e108, + .halt_reg = 0x6e018, .clkr = { - .enable_reg = 0x6e108, + .enable_reg = 0x6e018, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_pcie_2_pipe_clk", @@ -3329,6 +3329,8 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = { [GCC_USB_20_BCR] = { 0x12000 }, [GCC_QUSB2PHY_PRIM_BCR] = { 0x12038 }, [GCC_QUSB2PHY_SEC_BCR] = { 0x1203c }, + [GCC_USB3_PHY_BCR] = { 0x50020 }, + [GCC_USB3PHY_PHY_BCR] = { 0x50024 }, [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 }, [GCC_SDCC1_BCR] = { 0x13000 }, [GCC_SDCC2_BCR] = { 0x14000 }, @@ -3404,6 +3406,8 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = { [GCC_PCIE_2_BCR] = { 0x6e000 }, [GCC_PCIE_2_PHY_BCR] = { 0x6e038 }, [GCC_PCIE_PHY_BCR] = { 0x6f000 }, + [GCC_PCIE_PHY_COM_BCR] = { 0x6f014 }, + [GCC_PCIE_PHY_COM_NOCSR_BCR] = { 0x6f00c }, [GCC_DCD_BCR] = { 0x70000 }, [GCC_OBT_ODT_BCR] = { 0x73000 }, [GCC_UFS_BCR] = { 0x75000 }, @@ -3447,9 +3451,8 @@ MODULE_DEVICE_TABLE(of, gcc_msm8996_match_table); static int gcc_msm8996_probe(struct platform_device *pdev) { - struct clk *clk; struct device *dev = &pdev->dev; - int i; + int i, ret; struct regmap *regmap; regmap = qcom_cc_map(pdev, &gcc_msm8996_desc); @@ -3463,9 +3466,9 @@ static int gcc_msm8996_probe(struct platform_device *pdev) regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21)); for (i = 0; i < ARRAY_SIZE(gcc_msm8996_hws); i++) { - clk = devm_clk_register(dev, gcc_msm8996_hws[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(dev, gcc_msm8996_hws[i]); + if (ret) + return ret; } return qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap); diff --git a/drivers/clk/qcom/lcc-mdm9615.c b/drivers/clk/qcom/lcc-mdm9615.c new file mode 100644 index 0000000..3237ef4 --- /dev/null +++ b/drivers/clk/qcom/lcc-mdm9615.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) BayLibre, SAS. + * Author : Neil Armstrong <narmstrong@baylibre.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,lcc-mdm9615.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" + +static struct clk_pll pll4 = { + .l_reg = 0x4, + .m_reg = 0x8, + .n_reg = 0xc, + .config_reg = 0x14, + .mode_reg = 0x0, + .status_reg = 0x18, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll4", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +enum { + P_CXO, + P_PLL4, +}; + +static const struct parent_map lcc_cxo_pll4_map[] = { + { P_CXO, 0 }, + { P_PLL4, 2 } +}; + +static const char * const lcc_cxo_pll4[] = { + "cxo", + "pll4_vote", +}; + +static struct freq_tbl clk_tbl_aif_osr_492[] = { + { 512000, P_PLL4, 4, 1, 240 }, + { 768000, P_PLL4, 4, 1, 160 }, + { 1024000, P_PLL4, 4, 1, 120 }, + { 1536000, P_PLL4, 4, 1, 80 }, + { 2048000, P_PLL4, 4, 1, 60 }, + { 3072000, P_PLL4, 4, 1, 40 }, + { 4096000, P_PLL4, 4, 1, 30 }, + { 6144000, P_PLL4, 4, 1, 20 }, + { 8192000, P_PLL4, 4, 1, 15 }, + { 12288000, P_PLL4, 4, 1, 10 }, + { 24576000, P_PLL4, 4, 1, 5 }, + { 27000000, P_CXO, 1, 0, 0 }, + { } +}; + +static struct freq_tbl clk_tbl_aif_osr_393[] = { + { 512000, P_PLL4, 4, 1, 192 }, + { 768000, P_PLL4, 4, 1, 128 }, + { 1024000, P_PLL4, 4, 1, 96 }, + { 1536000, P_PLL4, 4, 1, 64 }, + { 2048000, P_PLL4, 4, 1, 48 }, + { 3072000, P_PLL4, 4, 1, 32 }, + { 4096000, P_PLL4, 4, 1, 24 }, + { 6144000, P_PLL4, 4, 1, 16 }, + { 8192000, P_PLL4, 4, 1, 12 }, + { 12288000, P_PLL4, 4, 1, 8 }, + { 24576000, P_PLL4, 4, 1, 4 }, + { 27000000, P_CXO, 1, 0, 0 }, + { } +}; + +static struct clk_rcg mi2s_osr_src = { + .ns_reg = 0x48, + .md_reg = 0x4c, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 24, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = lcc_cxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_osr_393, + .clkr = { + .enable_reg = 0x48, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "mi2s_osr_src", + .parent_names = lcc_cxo_pll4, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static const char * const lcc_mi2s_parents[] = { + "mi2s_osr_src", +}; + +static struct clk_branch mi2s_osr_clk = { + .halt_reg = 0x50, + .halt_bit = 1, + .halt_check = BRANCH_HALT_ENABLE, + .clkr = { + .enable_reg = 0x48, + .enable_mask = BIT(17), + .hw.init = &(struct clk_init_data){ + .name = "mi2s_osr_clk", + .parent_names = lcc_mi2s_parents, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap_div mi2s_div_clk = { + .reg = 0x48, + .shift = 10, + .width = 4, + .clkr = { + .enable_reg = 0x48, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data){ + .name = "mi2s_div_clk", + .parent_names = lcc_mi2s_parents, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, + }, +}; + +static struct clk_branch mi2s_bit_div_clk = { + .halt_reg = 0x50, + .halt_bit = 0, + .halt_check = BRANCH_HALT_ENABLE, + .clkr = { + .enable_reg = 0x48, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data){ + .name = "mi2s_bit_div_clk", + .parent_names = (const char *[]){ "mi2s_div_clk" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap_mux mi2s_bit_clk = { + .reg = 0x48, + .shift = 14, + .width = 1, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "mi2s_bit_clk", + .parent_names = (const char *[]){ + "mi2s_bit_div_clk", + "mi2s_codec_clk", + }, + .num_parents = 2, + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +#define CLK_AIF_OSR_DIV(prefix, _ns, _md, hr) \ +static struct clk_rcg prefix##_osr_src = { \ + .ns_reg = _ns, \ + .md_reg = _md, \ + .mn = { \ + .mnctr_en_bit = 8, \ + .mnctr_reset_bit = 7, \ + .mnctr_mode_shift = 5, \ + .n_val_shift = 24, \ + .m_val_shift = 8, \ + .width = 8, \ + }, \ + .p = { \ + .pre_div_shift = 3, \ + .pre_div_width = 2, \ + }, \ + .s = { \ + .src_sel_shift = 0, \ + .parent_map = lcc_cxo_pll4_map, \ + }, \ + .freq_tbl = clk_tbl_aif_osr_393, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(9), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_osr_src", \ + .parent_names = lcc_cxo_pll4, \ + .num_parents = 2, \ + .ops = &clk_rcg_ops, \ + .flags = CLK_SET_RATE_GATE, \ + }, \ + }, \ +}; \ + \ +static const char * const lcc_##prefix##_parents[] = { \ + #prefix "_osr_src", \ +}; \ + \ +static struct clk_branch prefix##_osr_clk = { \ + .halt_reg = hr, \ + .halt_bit = 1, \ + .halt_check = BRANCH_HALT_ENABLE, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(21), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_osr_clk", \ + .parent_names = lcc_##prefix##_parents, \ + .num_parents = 1, \ + .ops = &clk_branch_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + \ +static struct clk_regmap_div prefix##_div_clk = { \ + .reg = _ns, \ + .shift = 10, \ + .width = 8, \ + .clkr = { \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_div_clk", \ + .parent_names = lcc_##prefix##_parents, \ + .num_parents = 1, \ + .ops = &clk_regmap_div_ops, \ + }, \ + }, \ +}; \ + \ +static struct clk_branch prefix##_bit_div_clk = { \ + .halt_reg = hr, \ + .halt_bit = 0, \ + .halt_check = BRANCH_HALT_ENABLE, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(19), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_bit_div_clk", \ + .parent_names = (const char *[]){ \ + #prefix "_div_clk" \ + }, \ + .num_parents = 1, \ + .ops = &clk_branch_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + \ +static struct clk_regmap_mux prefix##_bit_clk = { \ + .reg = _ns, \ + .shift = 18, \ + .width = 1, \ + .clkr = { \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_bit_clk", \ + .parent_names = (const char *[]){ \ + #prefix "_bit_div_clk", \ + #prefix "_codec_clk", \ + }, \ + .num_parents = 2, \ + .ops = &clk_regmap_mux_closest_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +} + +CLK_AIF_OSR_DIV(codec_i2s_mic, 0x60, 0x64, 0x68); +CLK_AIF_OSR_DIV(spare_i2s_mic, 0x78, 0x7c, 0x80); +CLK_AIF_OSR_DIV(codec_i2s_spkr, 0x6c, 0x70, 0x74); +CLK_AIF_OSR_DIV(spare_i2s_spkr, 0x84, 0x88, 0x8c); + +static struct freq_tbl clk_tbl_pcm_492[] = { + { 256000, P_PLL4, 4, 1, 480 }, + { 512000, P_PLL4, 4, 1, 240 }, + { 768000, P_PLL4, 4, 1, 160 }, + { 1024000, P_PLL4, 4, 1, 120 }, + { 1536000, P_PLL4, 4, 1, 80 }, + { 2048000, P_PLL4, 4, 1, 60 }, + { 3072000, P_PLL4, 4, 1, 40 }, + { 4096000, P_PLL4, 4, 1, 30 }, + { 6144000, P_PLL4, 4, 1, 20 }, + { 8192000, P_PLL4, 4, 1, 15 }, + { 12288000, P_PLL4, 4, 1, 10 }, + { 24576000, P_PLL4, 4, 1, 5 }, + { 27000000, P_CXO, 1, 0, 0 }, + { } +}; + +static struct freq_tbl clk_tbl_pcm_393[] = { + { 256000, P_PLL4, 4, 1, 384 }, + { 512000, P_PLL4, 4, 1, 192 }, + { 768000, P_PLL4, 4, 1, 128 }, + { 1024000, P_PLL4, 4, 1, 96 }, + { 1536000, P_PLL4, 4, 1, 64 }, + { 2048000, P_PLL4, 4, 1, 48 }, + { 3072000, P_PLL4, 4, 1, 32 }, + { 4096000, P_PLL4, 4, 1, 24 }, + { 6144000, P_PLL4, 4, 1, 16 }, + { 8192000, P_PLL4, 4, 1, 12 }, + { 12288000, P_PLL4, 4, 1, 8 }, + { 24576000, P_PLL4, 4, 1, 4 }, + { 27000000, P_CXO, 1, 0, 0 }, + { } +}; + +static struct clk_rcg pcm_src = { + .ns_reg = 0x54, + .md_reg = 0x58, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = lcc_cxo_pll4_map, + }, + .freq_tbl = clk_tbl_pcm_393, + .clkr = { + .enable_reg = 0x54, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pcm_src", + .parent_names = lcc_cxo_pll4, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch pcm_clk_out = { + .halt_reg = 0x5c, + .halt_bit = 0, + .halt_check = BRANCH_HALT_ENABLE, + .clkr = { + .enable_reg = 0x54, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "pcm_clk_out", + .parent_names = (const char *[]){ "pcm_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap_mux pcm_clk = { + .reg = 0x54, + .shift = 10, + .width = 1, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "pcm_clk", + .parent_names = (const char *[]){ + "pcm_clk_out", + "pcm_codec_clk", + }, + .num_parents = 2, + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg slimbus_src = { + .ns_reg = 0xcc, + .md_reg = 0xd0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 24, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = lcc_cxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_osr_393, + .clkr = { + .enable_reg = 0xcc, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "slimbus_src", + .parent_names = lcc_cxo_pll4, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static const char * const lcc_slimbus_parents[] = { + "slimbus_src", +}; + +static struct clk_branch audio_slimbus_clk = { + .halt_reg = 0xd4, + .halt_bit = 0, + .halt_check = BRANCH_HALT_ENABLE, + .clkr = { + .enable_reg = 0xcc, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "audio_slimbus_clk", + .parent_names = lcc_slimbus_parents, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sps_slimbus_clk = { + .halt_reg = 0xd4, + .halt_bit = 1, + .halt_check = BRANCH_HALT_ENABLE, + .clkr = { + .enable_reg = 0xcc, + .enable_mask = BIT(12), + .hw.init = &(struct clk_init_data){ + .name = "sps_slimbus_clk", + .parent_names = lcc_slimbus_parents, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap *lcc_mdm9615_clks[] = { + [PLL4] = &pll4.clkr, + [MI2S_OSR_SRC] = &mi2s_osr_src.clkr, + [MI2S_OSR_CLK] = &mi2s_osr_clk.clkr, + [MI2S_DIV_CLK] = &mi2s_div_clk.clkr, + [MI2S_BIT_DIV_CLK] = &mi2s_bit_div_clk.clkr, + [MI2S_BIT_CLK] = &mi2s_bit_clk.clkr, + [PCM_SRC] = &pcm_src.clkr, + [PCM_CLK_OUT] = &pcm_clk_out.clkr, + [PCM_CLK] = &pcm_clk.clkr, + [SLIMBUS_SRC] = &slimbus_src.clkr, + [AUDIO_SLIMBUS_CLK] = &audio_slimbus_clk.clkr, + [SPS_SLIMBUS_CLK] = &sps_slimbus_clk.clkr, + [CODEC_I2S_MIC_OSR_SRC] = &codec_i2s_mic_osr_src.clkr, + [CODEC_I2S_MIC_OSR_CLK] = &codec_i2s_mic_osr_clk.clkr, + [CODEC_I2S_MIC_DIV_CLK] = &codec_i2s_mic_div_clk.clkr, + [CODEC_I2S_MIC_BIT_DIV_CLK] = &codec_i2s_mic_bit_div_clk.clkr, + [CODEC_I2S_MIC_BIT_CLK] = &codec_i2s_mic_bit_clk.clkr, + [SPARE_I2S_MIC_OSR_SRC] = &spare_i2s_mic_osr_src.clkr, + [SPARE_I2S_MIC_OSR_CLK] = &spare_i2s_mic_osr_clk.clkr, + [SPARE_I2S_MIC_DIV_CLK] = &spare_i2s_mic_div_clk.clkr, + [SPARE_I2S_MIC_BIT_DIV_CLK] = &spare_i2s_mic_bit_div_clk.clkr, + [SPARE_I2S_MIC_BIT_CLK] = &spare_i2s_mic_bit_clk.clkr, + [CODEC_I2S_SPKR_OSR_SRC] = &codec_i2s_spkr_osr_src.clkr, + [CODEC_I2S_SPKR_OSR_CLK] = &codec_i2s_spkr_osr_clk.clkr, + [CODEC_I2S_SPKR_DIV_CLK] = &codec_i2s_spkr_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_DIV_CLK] = &codec_i2s_spkr_bit_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_CLK] = &codec_i2s_spkr_bit_clk.clkr, + [SPARE_I2S_SPKR_OSR_SRC] = &spare_i2s_spkr_osr_src.clkr, + [SPARE_I2S_SPKR_OSR_CLK] = &spare_i2s_spkr_osr_clk.clkr, + [SPARE_I2S_SPKR_DIV_CLK] = &spare_i2s_spkr_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_DIV_CLK] = &spare_i2s_spkr_bit_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_CLK] = &spare_i2s_spkr_bit_clk.clkr, +}; + +static const struct regmap_config lcc_mdm9615_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xfc, + .fast_io = true, +}; + +static const struct qcom_cc_desc lcc_mdm9615_desc = { + .config = &lcc_mdm9615_regmap_config, + .clks = lcc_mdm9615_clks, + .num_clks = ARRAY_SIZE(lcc_mdm9615_clks), +}; + +static const struct of_device_id lcc_mdm9615_match_table[] = { + { .compatible = "qcom,lcc-mdm9615" }, + { } +}; +MODULE_DEVICE_TABLE(of, lcc_mdm9615_match_table); + +static int lcc_mdm9615_probe(struct platform_device *pdev) +{ + u32 val; + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &lcc_mdm9615_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Use the correct frequency plan depending on speed of PLL4 */ + regmap_read(regmap, 0x4, &val); + if (val == 0x12) { + slimbus_src.freq_tbl = clk_tbl_aif_osr_492; + mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492; + codec_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492; + spare_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492; + codec_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492; + spare_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492; + pcm_src.freq_tbl = clk_tbl_pcm_492; + } + /* Enable PLL4 source on the LPASS Primary PLL Mux */ + regmap_write(regmap, 0xc4, 0x1); + + return qcom_cc_really_probe(pdev, &lcc_mdm9615_desc, regmap); +} + +static struct platform_driver lcc_mdm9615_driver = { + .probe = lcc_mdm9615_probe, + .driver = { + .name = "lcc-mdm9615", + .of_match_table = lcc_mdm9615_match_table, + }, +}; +module_platform_driver(lcc_mdm9615_driver); + +MODULE_DESCRIPTION("QCOM LCC MDM9615 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lcc-mdm9615"); diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 847dd9d..ca97e11 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -2888,6 +2888,14 @@ static struct clk_hw *mmcc_msm8996_hws[] = { &gpll0_div.hw, }; +static struct gdsc mmagic_bimc_gdsc = { + .gdscr = 0x529c, + .pd = { + .name = "mmagic_bimc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct gdsc mmagic_video_gdsc = { .gdscr = 0x119c, .gds_hw_ctrl = 0x120c, @@ -3201,6 +3209,7 @@ static struct clk_regmap *mmcc_msm8996_clocks[] = { }; static struct gdsc *mmcc_msm8996_gdscs[] = { + [MMAGIC_BIMC_GDSC] = &mmagic_bimc_gdsc, [MMAGIC_VIDEO_GDSC] = &mmagic_video_gdsc, [MMAGIC_MDSS_GDSC] = &mmagic_mdss_gdsc, [MMAGIC_CAMSS_GDSC] = &mmagic_camss_gdsc, @@ -3305,9 +3314,8 @@ MODULE_DEVICE_TABLE(of, mmcc_msm8996_match_table); static int mmcc_msm8996_probe(struct platform_device *pdev) { - struct clk *clk; struct device *dev = &pdev->dev; - int i; + int i, ret; struct regmap *regmap; regmap = qcom_cc_map(pdev, &mmcc_msm8996_desc); @@ -3320,9 +3328,9 @@ static int mmcc_msm8996_probe(struct platform_device *pdev) regmap_update_bits(regmap, 0x5054, BIT(15), 0); for (i = 0; i < ARRAY_SIZE(mmcc_msm8996_hws); i++) { - clk = devm_clk_register(dev, mmcc_msm8996_hws[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ret = devm_clk_hw_register(dev, mmcc_msm8996_hws[i]); + if (ret) + return ret; } return qcom_cc_really_probe(pdev, &mmcc_msm8996_desc, regmap); diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 5093a25..9375777 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -167,7 +167,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) unsigned int i; group = kzalloc(sizeof(*group), GFP_KERNEL); - clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL); + clks = kmalloc_array(MSTP_MAX_CLOCKS, sizeof(*clks), GFP_KERNEL); if (group == NULL || clks == NULL) { kfree(group); kfree(clks); diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c index f6312c6..5adb934 100644 --- a/drivers/clk/renesas/clk-rz.c +++ b/drivers/clk/renesas/clk-rz.c @@ -25,10 +25,31 @@ struct rz_cpg { #define CPG_FRQCR 0x10 #define CPG_FRQCR2 0x14 +#define PPR0 0xFCFE3200 +#define PIBC0 0xFCFE7000 + +#define MD_CLK(x) ((x >> 2) & 1) /* P0_2 */ + /* ----------------------------------------------------------------------------- * Initialization */ +static u16 __init rz_cpg_read_mode_pins(void) +{ + void __iomem *ppr0, *pibc0; + u16 modes; + + ppr0 = ioremap_nocache(PPR0, 2); + pibc0 = ioremap_nocache(PIBC0, 2); + BUG_ON(!ppr0 || !pibc0); + iowrite16(4, pibc0); /* enable input buffer */ + modes = ioread16(ppr0); + iounmap(ppr0); + iounmap(pibc0); + + return modes; +} + static struct clk * __init rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name) { @@ -37,8 +58,7 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 }; if (strcmp(name, "pll") == 0) { - /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */ - unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */ + unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins()); const char *parent_name = of_clk_get_parent_name(np, cpg_mode); mult = cpg_mode ? (32 / 4) : 30; diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index e38bf60..f255e45 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -123,6 +123,10 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("sys-dmac2", 217, R8A7795_CLK_S3D1), DEF_MOD("sys-dmac1", 218, R8A7795_CLK_S3D1), DEF_MOD("sys-dmac0", 219, R8A7795_CLK_S3D1), + DEF_MOD("cmt3", 300, R8A7795_CLK_R), + DEF_MOD("cmt2", 301, R8A7795_CLK_R), + DEF_MOD("cmt1", 302, R8A7795_CLK_R), + DEF_MOD("cmt0", 303, R8A7795_CLK_R), DEF_MOD("scif2", 310, R8A7795_CLK_S3D4), DEF_MOD("sdif3", 311, R8A7795_CLK_SD3), DEF_MOD("sdif2", 312, R8A7795_CLK_SD2), diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index c84b549..eb347ed 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -45,6 +45,7 @@ enum clk_ids { CLK_S3, CLK_SDSRC, CLK_SSPSRC, + CLK_RINT, /* Module Clocks */ MOD_CLK_BASE @@ -69,6 +70,7 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1), DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), /* Core Clock Outputs */ DEF_FIXED("ztr", R8A7796_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), @@ -92,13 +94,42 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_FIXED("s3d2", R8A7796_CLK_S3D2, CLK_S3, 2, 1), DEF_FIXED("s3d4", R8A7796_CLK_S3D4, CLK_S3, 4, 1), + DEF_GEN3_SD("sd0", R8A7796_CLK_SD0, CLK_SDSRC, 0x0074), + DEF_GEN3_SD("sd1", R8A7796_CLK_SD1, CLK_SDSRC, 0x0078), + DEF_GEN3_SD("sd2", R8A7796_CLK_SD2, CLK_SDSRC, 0x0268), + DEF_GEN3_SD("sd3", R8A7796_CLK_SD3, CLK_SDSRC, 0x026c), + DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1), DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1), + + DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8), + DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32), + + DEF_BASE("r", R8A7796_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT), }; static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { + DEF_MOD("cmt3", 300, R8A7796_CLK_R), + DEF_MOD("cmt2", 301, R8A7796_CLK_R), + DEF_MOD("cmt1", 302, R8A7796_CLK_R), + DEF_MOD("cmt0", 303, R8A7796_CLK_R), DEF_MOD("scif2", 310, R8A7796_CLK_S3D4), + DEF_MOD("sdif3", 311, R8A7796_CLK_SD3), + DEF_MOD("sdif2", 312, R8A7796_CLK_SD2), + DEF_MOD("sdif1", 313, R8A7796_CLK_SD1), + DEF_MOD("sdif0", 314, R8A7796_CLK_SD0), + DEF_MOD("rwdt0", 402, R8A7796_CLK_R), DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1), + DEF_MOD("thermal", 522, R8A7796_CLK_CP), + DEF_MOD("etheravb", 812, R8A7796_CLK_S0D6), + DEF_MOD("gpio7", 905, R8A7796_CLK_S3D4), + DEF_MOD("gpio6", 906, R8A7796_CLK_S3D4), + DEF_MOD("gpio5", 907, R8A7796_CLK_S3D4), + DEF_MOD("gpio4", 908, R8A7796_CLK_S3D4), + DEF_MOD("gpio3", 909, R8A7796_CLK_S3D4), + DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4), + DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4), + DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4), }; static const unsigned int r8a7796_crit_mod_clks[] __initconst = { diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f47a2fa..b5f2c8e 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3036.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c new file mode 100644 index 0000000..8feba93 --- /dev/null +++ b/drivers/clk/rockchip/clk-ddr.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Lin Huang <hl@rock-chips.com> + * + * 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/arm-smccc.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <soc/rockchip/rockchip_sip.h> +#include "clk.h" + +struct rockchip_ddrclk { + struct clk_hw hw; + void __iomem *reg_base; + int mux_offset; + int mux_shift; + int mux_width; + int div_shift; + int div_width; + int ddr_flag; + spinlock_t *lock; +}; + +#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) + +static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + unsigned long flags; + struct arm_smccc_res res; + + spin_lock_irqsave(ddrclk->lock, flags); + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, + ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, &res); + spin_unlock_irqrestore(ddrclk->lock, flags); + + return res.a0; +} + +static unsigned long +rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, + ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, &res); + + return res.a0; +} + +static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, + ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, + 0, 0, 0, 0, &res); + + return res.a0; +} + +static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 val; + + val = clk_readl(ddrclk->reg_base + + ddrclk->mux_offset) >> ddrclk->mux_shift; + val &= GENMASK(ddrclk->mux_width - 1, 0); + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static const struct clk_ops rockchip_ddrclk_sip_ops = { + .recalc_rate = rockchip_ddrclk_sip_recalc_rate, + .set_rate = rockchip_ddrclk_sip_set_rate, + .round_rate = rockchip_ddrclk_sip_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, + const char *const *parent_names, + u8 num_parents, int mux_offset, + int mux_shift, int mux_width, + int div_shift, int div_width, + int ddr_flag, void __iomem *reg_base, + spinlock_t *lock) +{ + struct rockchip_ddrclk *ddrclk; + struct clk_init_data init; + struct clk *clk; + + ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL); + if (!ddrclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = parent_names; + init.num_parents = num_parents; + + init.flags = flags; + init.flags |= CLK_SET_RATE_NO_REPARENT; + + switch (ddr_flag) { + case ROCKCHIP_DDRCLK_SIP: + init.ops = &rockchip_ddrclk_sip_ops; + break; + default: + pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag); + kfree(ddrclk); + return ERR_PTR(-EINVAL); + } + + ddrclk->reg_base = reg_base; + ddrclk->lock = lock; + ddrclk->hw.init = &init; + ddrclk->mux_offset = mux_offset; + ddrclk->mux_shift = mux_shift; + ddrclk->mux_width = mux_width; + ddrclk->div_shift = div_shift; + ddrclk->div_width = div_width; + ddrclk->ddr_flag = ddr_flag; + + clk = clk_register(NULL, &ddrclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register ddrclk %s\n", __func__, name); + kfree(ddrclk); + return NULL; + } + + return clk; +} diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index db81e45..9c1373e 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -837,7 +837,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, u8 num_parents, int con_offset, int grf_lock_offset, int lock_shift, int mode_offset, int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags) + unsigned long flags, u8 clk_pll_flags) { const char *pll_parents[3]; struct clk_init_data init; @@ -892,7 +892,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, init.name = pll_name; /* keep all plls untouched for now */ - init.flags = CLK_IGNORE_UNUSED; + init.flags = flags | CLK_IGNORE_UNUSED; init.parent_names = &parent_names[0]; init.num_parents = 1; diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index cdfabeb..8387c7a 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -100,8 +100,10 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = { RK3036_PLL_RATE( 297000000, 1, 99, 4, 2, 1, 0), RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0), RK3036_PLL_RATE( 148500000, 1, 99, 4, 4, 1, 0), + RK3036_PLL_RATE( 106500000, 1, 71, 4, 4, 1, 0), RK3036_PLL_RATE( 96000000, 1, 64, 4, 4, 1, 0), RK3036_PLL_RATE( 74250000, 2, 99, 4, 4, 1, 0), + RK3036_PLL_RATE( 65000000, 1, 65, 6, 4, 1, 0), RK3036_PLL_RATE( 54000000, 1, 54, 6, 4, 1, 0), RK3036_PLL_RATE( 27000000, 1, 27, 6, 4, 1, 0), { /* sentinel */ }, @@ -118,6 +120,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", "clk_core_b_bpll_src", "clk_core_b_dpll_src", "clk_core_b_gpll_src" }; +PNAME(mux_ddrclk_p) = { "clk_ddrc_lpll_src", + "clk_ddrc_bpll_src", + "clk_ddrc_dpll_src", + "clk_ddrc_gpll_src" }; PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", @@ -373,6 +379,7 @@ static struct rockchip_cpuclk_rate_table rk3399_cpuclkb_rates[] __initdata = { RK3399_CPUCLKB_RATE(2184000000, 1, 11, 11), RK3399_CPUCLKB_RATE(2088000000, 1, 10, 10), RK3399_CPUCLKB_RATE(2040000000, 1, 10, 10), + RK3399_CPUCLKB_RATE(2016000000, 1, 9, 9), RK3399_CPUCLKB_RATE(1992000000, 1, 9, 9), RK3399_CPUCLKB_RATE(1896000000, 1, 9, 9), RK3399_CPUCLKB_RATE(1800000000, 1, 8, 8), @@ -578,7 +585,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE(0, "clk_spdif_div", mux_pll_src_cpll_gpll_p, 0, RK3399_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 7, DFLAGS, RK3399_CLKGATE_CON(8), 13, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", 0, RK3399_CLKSEL_CON(99), 0, RK3399_CLKGATE_CON(8), 14, GFLAGS, &rk3399_spdif_fracmux), @@ -592,7 +599,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE(0, "clk_i2s0_div", mux_pll_src_cpll_gpll_p, 0, RK3399_CLKSEL_CON(28), 7, 1, MFLAGS, 0, 7, DFLAGS, RK3399_CLKGATE_CON(8), 3, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", 0, RK3399_CLKSEL_CON(96), 0, RK3399_CLKGATE_CON(8), 4, GFLAGS, &rk3399_i2s0_fracmux), @@ -602,7 +609,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE(0, "clk_i2s1_div", mux_pll_src_cpll_gpll_p, 0, RK3399_CLKSEL_CON(29), 7, 1, MFLAGS, 0, 7, DFLAGS, RK3399_CLKGATE_CON(8), 6, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", 0, RK3399_CLKSEL_CON(97), 0, RK3399_CLKGATE_CON(8), 7, GFLAGS, &rk3399_i2s1_fracmux), @@ -612,7 +619,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE(0, "clk_i2s2_div", mux_pll_src_cpll_gpll_p, 0, RK3399_CLKSEL_CON(30), 7, 1, MFLAGS, 0, 7, DFLAGS, RK3399_CLKGATE_CON(8), 9, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", 0, RK3399_CLKSEL_CON(98), 0, RK3399_CLKGATE_CON(8), 10, GFLAGS, &rk3399_i2s2_fracmux), @@ -631,7 +638,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_uart0_div", "clk_uart0_src", 0, RK3399_CLKSEL_CON(33), 0, 7, DFLAGS, RK3399_CLKGATE_CON(9), 0, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", 0, RK3399_CLKSEL_CON(100), 0, RK3399_CLKGATE_CON(9), 1, GFLAGS, &rk3399_uart0_fracmux), @@ -641,7 +648,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_uart1_div", "clk_uart_src", 0, RK3399_CLKSEL_CON(34), 0, 7, DFLAGS, RK3399_CLKGATE_CON(9), 2, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", 0, RK3399_CLKSEL_CON(101), 0, RK3399_CLKGATE_CON(9), 3, GFLAGS, &rk3399_uart1_fracmux), @@ -649,7 +656,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_uart2_div", "clk_uart_src", 0, RK3399_CLKSEL_CON(35), 0, 7, DFLAGS, RK3399_CLKGATE_CON(9), 4, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", 0, RK3399_CLKSEL_CON(102), 0, RK3399_CLKGATE_CON(9), 5, GFLAGS, &rk3399_uart2_fracmux), @@ -657,7 +664,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_uart3_div", "clk_uart_src", 0, RK3399_CLKSEL_CON(36), 0, 7, DFLAGS, RK3399_CLKGATE_CON(9), 6, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_div", 0, RK3399_CLKSEL_CON(103), 0, RK3399_CLKGATE_CON(9), 7, GFLAGS, &rk3399_uart3_fracmux), @@ -846,9 +853,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKSEL_CON(14), 12, 2, DFLAGS, RK3399_CLKGATE_CON(5), 4, GFLAGS), - GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLK_IGNORE_UNUSED, + GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", 0, RK3399_CLKGATE_CON(20), 2, GFLAGS), - GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLK_IGNORE_UNUSED, + GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", 0, RK3399_CLKGATE_CON(20), 10, GFLAGS), GATE(0, "aclk_perihp_noc", "aclk_perihp", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(20), 12, GFLAGS), @@ -1161,7 +1168,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKSEL_CON(49), 8, 2, MFLAGS, 0, 8, DFLAGS, RK3399_CLKGATE_CON(10), 12, GFLAGS), - COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop0_frac", "dclk_vop0_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX_NOGATE(DCLK_VOP0_FRAC, "dclk_vop0_frac", "dclk_vop0_div", 0, RK3399_CLKSEL_CON(106), 0, &rk3399_dclk_vop0_fracmux), @@ -1191,7 +1198,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKSEL_CON(50), 8, 2, MFLAGS, 0, 8, DFLAGS, RK3399_CLKGATE_CON(10), 13, GFLAGS), - COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop1_frac", "dclk_vop1_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX_NOGATE(DCLK_VOP1_FRAC, "dclk_vop1_frac", "dclk_vop1_div", 0, RK3399_CLKSEL_CON(107), 0, &rk3399_dclk_vop1_fracmux), @@ -1305,7 +1312,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { /* testout */ MUX(0, "clk_test_pre", mux_pll_src_cpll_gpll_p, CLK_SET_RATE_PARENT, RK3399_CLKSEL_CON(58), 7, 1, MFLAGS), - COMPOSITE_FRAC(0, "clk_test_frac", "clk_test_pre", CLK_SET_RATE_PARENT, + COMPOSITE_FRAC(0, "clk_test_frac", "clk_test_pre", 0, RK3399_CLKSEL_CON(105), 0, RK3399_CLKGATE_CON(13), 9, GFLAGS), @@ -1377,6 +1384,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, RK3368_CLKGATE_CON(13), 11, GFLAGS), + + /* ddrc */ + GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), + 0, GFLAGS), + GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3), + 1, GFLAGS), + GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3), + 2, GFLAGS), + GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), + 3, GFLAGS), + COMPOSITE_DDRCLK(SCLK_DDRC, "sclk_ddrc", mux_ddrclk_p, 0, + RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { @@ -1398,7 +1417,7 @@ static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { RK3399_PMU_CLKSEL_CON(1), 13, 1, MFLAGS, 8, 5, DFLAGS, RK3399_PMU_CLKGATE_CON(0), 8, GFLAGS), - COMPOSITE_FRACMUX_NOGATE(0, "clk_wifi_frac", "clk_wifi_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX_NOGATE(0, "clk_wifi_frac", "clk_wifi_div", 0, RK3399_PMU_CLKSEL_CON(7), 0, &rk3399_pmuclk_wifi_fracmux), @@ -1426,7 +1445,7 @@ static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { RK3399_PMU_CLKSEL_CON(5), 10, 1, MFLAGS, 0, 7, DFLAGS, RK3399_PMU_CLKGATE_CON(0), 5, GFLAGS), - COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_div", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_div", 0, RK3399_PMU_CLKSEL_CON(6), 0, RK3399_PMU_CLKGATE_CON(0), 6, GFLAGS, &rk3399_uart4_pmu_fracmux), @@ -1468,6 +1487,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "aclk_cci_pre", "aclk_gic", "aclk_gic_noc", + "aclk_hdcp_noc", + "hclk_hdcp_noc", + "pclk_hdcp_noc", "pclk_perilp0", "pclk_perilp0", "hclk_perilp0", @@ -1488,6 +1510,10 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "gpll_hclk_perilp1_src", "gpll_aclk_perilp0_src", "gpll_aclk_perihp_src", + "aclk_vio_noc", + + /* ddrc */ + "sclk_ddrc" }; static const char *const rk3399_pmucru_critical_clocks[] __initconst = { diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c index 4cf838d5..2c9bb81 100644 --- a/drivers/clk/rockchip/clk-rockchip.c +++ b/drivers/clk/rockchip/clk-rockchip.c @@ -49,14 +49,19 @@ static void __init rk2928_gate_clk_init(struct device_node *node) } reg = of_iomap(node, 0); + if (!reg) + return; clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); - if (!clk_data) + if (!clk_data) { + iounmap(reg); return; + } clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) { kfree(clk_data); + iounmap(reg); return; } diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 7ffd134..b886be3 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -385,7 +385,7 @@ void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, list->con_offset, grf_lock_offset, list->lock_shift, list->mode_offset, list->mode_shift, list->rate_table, - list->pll_flags); + list->flags, list->pll_flags); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, list->name); @@ -484,6 +484,15 @@ void __init rockchip_clk_register_branches( list->gate_offset, list->gate_shift, list->gate_flags, flags, &ctx->lock); break; + case branch_ddrclk: + clk = rockchip_clk_register_ddrclk( + list->name, list->flags, + list->parent_names, list->num_parents, + list->muxdiv_offset, list->mux_shift, + list->mux_width, list->div_shift, + list->div_width, list->div_flags, + ctx->reg_base, &ctx->lock); + break; } /* none of the cases above matched */ diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 2194ffa..1653edd 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -238,7 +238,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, u8 num_parents, int con_offset, int grf_lock_offset, int lock_shift, int mode_offset, int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags); + unsigned long flags, u8 clk_pll_flags); struct rockchip_cpuclk_clksel { int reg; @@ -281,6 +281,20 @@ struct clk *rockchip_clk_register_mmc(const char *name, const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift); +/* + * DDRCLK flags, including method of setting the rate + * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate. + */ +#define ROCKCHIP_DDRCLK_SIP BIT(0) + +struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, + const char *const *parent_names, + u8 num_parents, int mux_offset, + int mux_shift, int mux_width, + int div_shift, int div_width, + int ddr_flags, void __iomem *reg_base, + spinlock_t *lock); + #define ROCKCHIP_INVERTER_HIWORD_MASK BIT(0) struct clk *rockchip_clk_register_inverter(const char *name, @@ -299,6 +313,7 @@ enum rockchip_clk_branch_type { branch_mmc, branch_inverter, branch_factor, + branch_ddrclk, }; struct rockchip_clk_branch { @@ -488,6 +503,24 @@ struct rockchip_clk_branch { .child = ch, \ } +#define COMPOSITE_DDRCLK(_id, cname, pnames, f, mo, ms, mw, \ + ds, dw, df) \ + { \ + .id = _id, \ + .branch_type = branch_ddrclk, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .muxdiv_offset = mo, \ + .mux_shift = ms, \ + .mux_width = mw, \ + .div_shift = ds, \ + .div_width = dw, \ + .div_flags = df, \ + .gate_offset = -1, \ + } + #define MUX(_id, cname, pnames, f, o, s, w, mf) \ { \ .id = _id, \ diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index bdf8b97..51d152f 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -14,18 +14,13 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/syscore_ops.h> #include <linux/module.h> #include <linux/platform_device.h> #include <dt-bindings/clock/exynos-audss-clk.h> -enum exynos_audss_clk_type { - TYPE_EXYNOS4210, - TYPE_EXYNOS5250, - TYPE_EXYNOS5420, -}; - static DEFINE_SPINLOCK(lock); static struct clk **clk_table; static void __iomem *reg_base; @@ -44,9 +39,9 @@ static struct clk *epll; #ifdef CONFIG_PM_SLEEP static unsigned long reg_save[][2] = { - {ASS_CLK_SRC, 0}, - {ASS_CLK_DIV, 0}, - {ASS_CLK_GATE, 0}, + { ASS_CLK_SRC, 0 }, + { ASS_CLK_DIV, 0 }, + { ASS_CLK_GATE, 0 }, }; static int exynos_audss_clk_suspend(void) @@ -73,14 +68,43 @@ static struct syscore_ops exynos_audss_clk_syscore_ops = { }; #endif /* CONFIG_PM_SLEEP */ +struct exynos_audss_clk_drvdata { + unsigned int has_adma_clk:1; + unsigned int has_mst_clk:1; + unsigned int enable_epll:1; + unsigned int num_clks; +}; + +static const struct exynos_audss_clk_drvdata exynos4210_drvdata = { + .num_clks = EXYNOS_AUDSS_MAX_CLKS - 1, +}; + +static const struct exynos_audss_clk_drvdata exynos5410_drvdata = { + .num_clks = EXYNOS_AUDSS_MAX_CLKS - 1, + .has_mst_clk = 1, +}; + +static const struct exynos_audss_clk_drvdata exynos5420_drvdata = { + .num_clks = EXYNOS_AUDSS_MAX_CLKS, + .has_adma_clk = 1, + .enable_epll = 1, +}; + static const struct of_device_id exynos_audss_clk_of_match[] = { - { .compatible = "samsung,exynos4210-audss-clock", - .data = (void *)TYPE_EXYNOS4210, }, - { .compatible = "samsung,exynos5250-audss-clock", - .data = (void *)TYPE_EXYNOS5250, }, - { .compatible = "samsung,exynos5420-audss-clock", - .data = (void *)TYPE_EXYNOS5420, }, - {}, + { + .compatible = "samsung,exynos4210-audss-clock", + .data = &exynos4210_drvdata, + }, { + .compatible = "samsung,exynos5250-audss-clock", + .data = &exynos4210_drvdata, + }, { + .compatible = "samsung,exynos5410-audss-clock", + .data = &exynos5410_drvdata, + }, { + .compatible = "samsung,exynos5420-audss-clock", + .data = &exynos5420_drvdata, + }, + { }, }; static void exynos_audss_clk_teardown(void) @@ -106,19 +130,17 @@ static void exynos_audss_clk_teardown(void) /* register exynos_audss clocks */ static int exynos_audss_clk_probe(struct platform_device *pdev) { - int i, ret = 0; - struct resource *res; const char *mout_audss_p[] = {"fin_pll", "fout_epll"}; const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"}; const char *sclk_pcm_p = "sclk_pcm0"; struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in; - const struct of_device_id *match; - enum exynos_audss_clk_type variant; + const struct exynos_audss_clk_drvdata *variant; + struct resource *res; + int i, ret = 0; - match = of_match_node(exynos_audss_clk_of_match, pdev->dev.of_node); - if (!match) + variant = of_device_get_match_data(&pdev->dev); + if (!variant) return -EINVAL; - variant = (enum exynos_audss_clk_type)match->data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg_base = devm_ioremap_resource(&pdev->dev, res); @@ -126,7 +148,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to map audss registers\n"); return PTR_ERR(reg_base); } - /* EPLL don't have to be enabled for boards other than Exynos5420 */ + epll = ERR_PTR(-ENODEV); clk_table = devm_kzalloc(&pdev->dev, @@ -136,10 +158,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) return -ENOMEM; clk_data.clks = clk_table; - if (variant == TYPE_EXYNOS5420) - clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS; - else - clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS - 1; + clk_data.clk_num = variant->num_clks; pll_ref = devm_clk_get(&pdev->dev, "pll_ref"); pll_in = devm_clk_get(&pdev->dev, "pll_in"); @@ -148,13 +167,13 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) if (!IS_ERR(pll_in)) { mout_audss_p[1] = __clk_get_name(pll_in); - if (variant == TYPE_EXYNOS5420) { + if (variant->enable_epll) { epll = pll_in; ret = clk_prepare_enable(epll); if (ret) { dev_err(&pdev->dev, - "failed to prepare the epll clock\n"); + "failed to prepare the epll clock\n"); return ret; } } @@ -210,7 +229,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) sclk_pcm_p, CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 5, 0, &lock); - if (variant == TYPE_EXYNOS5420) { + if (variant->has_adma_clk) { clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 9, 0, &lock); @@ -234,9 +253,6 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP register_syscore_ops(&exynos_audss_clk_syscore_ops); #endif - - dev_info(&pdev->dev, "setup completed\n"); - return 0; unregister: diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index a43642c..fd1d9bf 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -131,21 +131,21 @@ static const struct samsung_gate_clock aud_gate_clks[] __initconst = { EN_IP_AUD, 4, 0, 0), }; +static const struct samsung_cmu_info aud_cmu __initconst = { + .mux_clks = aud_mux_clks, + .nr_mux_clks = ARRAY_SIZE(aud_mux_clks), + .div_clks = aud_div_clks, + .nr_div_clks = ARRAY_SIZE(aud_div_clks), + .gate_clks = aud_gate_clks, + .nr_gate_clks = ARRAY_SIZE(aud_gate_clks), + .nr_clk_ids = AUD_NR_CLK, + .clk_regs = aud_clk_regs, + .nr_clk_regs = ARRAY_SIZE(aud_clk_regs), +}; + static void __init exynos5260_clk_aud_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = aud_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks); - cmu.div_clks = aud_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(aud_div_clks); - cmu.gate_clks = aud_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(aud_gate_clks); - cmu.nr_clk_ids = AUD_NR_CLK; - cmu.clk_regs = aud_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(aud_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &aud_cmu); } CLK_OF_DECLARE(exynos5260_clk_aud, "samsung,exynos5260-clock-aud", @@ -321,21 +321,21 @@ static const struct samsung_gate_clock disp_gate_clks[] __initconst = { EN_IP_DISP, 25, 0, 0), }; +static const struct samsung_cmu_info disp_cmu __initconst = { + .mux_clks = disp_mux_clks, + .nr_mux_clks = ARRAY_SIZE(disp_mux_clks), + .div_clks = disp_div_clks, + .nr_div_clks = ARRAY_SIZE(disp_div_clks), + .gate_clks = disp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(disp_gate_clks), + .nr_clk_ids = DISP_NR_CLK, + .clk_regs = disp_clk_regs, + .nr_clk_regs = ARRAY_SIZE(disp_clk_regs), +}; + static void __init exynos5260_clk_disp_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = disp_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks); - cmu.div_clks = disp_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(disp_div_clks); - cmu.gate_clks = disp_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(disp_gate_clks); - cmu.nr_clk_ids = DISP_NR_CLK; - cmu.clk_regs = disp_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(disp_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &disp_cmu); } CLK_OF_DECLARE(exynos5260_clk_disp, "samsung,exynos5260-clock-disp", @@ -385,21 +385,21 @@ static const struct samsung_pll_clock egl_pll_clks[] __initconst = { pll2550_24mhz_tbl), }; +static const struct samsung_cmu_info egl_cmu __initconst = { + .pll_clks = egl_pll_clks, + .nr_pll_clks = ARRAY_SIZE(egl_pll_clks), + .mux_clks = egl_mux_clks, + .nr_mux_clks = ARRAY_SIZE(egl_mux_clks), + .div_clks = egl_div_clks, + .nr_div_clks = ARRAY_SIZE(egl_div_clks), + .nr_clk_ids = EGL_NR_CLK, + .clk_regs = egl_clk_regs, + .nr_clk_regs = ARRAY_SIZE(egl_clk_regs), +}; + static void __init exynos5260_clk_egl_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.pll_clks = egl_pll_clks; - cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks); - cmu.mux_clks = egl_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(egl_mux_clks); - cmu.div_clks = egl_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(egl_div_clks); - cmu.nr_clk_ids = EGL_NR_CLK; - cmu.clk_regs = egl_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(egl_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &egl_cmu); } CLK_OF_DECLARE(exynos5260_clk_egl, "samsung,exynos5260-clock-egl", @@ -487,19 +487,19 @@ static const struct samsung_gate_clock fsys_gate_clks[] __initconst = { EN_IP_FSYS_SECURE_SMMU_RTIC, 12, 0, 0), }; +static const struct samsung_cmu_info fsys_cmu __initconst = { + .mux_clks = fsys_mux_clks, + .nr_mux_clks = ARRAY_SIZE(fsys_mux_clks), + .gate_clks = fsys_gate_clks, + .nr_gate_clks = ARRAY_SIZE(fsys_gate_clks), + .nr_clk_ids = FSYS_NR_CLK, + .clk_regs = fsys_clk_regs, + .nr_clk_regs = ARRAY_SIZE(fsys_clk_regs), +}; + static void __init exynos5260_clk_fsys_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = fsys_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks); - cmu.gate_clks = fsys_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(fsys_gate_clks); - cmu.nr_clk_ids = FSYS_NR_CLK; - cmu.clk_regs = fsys_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(fsys_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &fsys_cmu); } CLK_OF_DECLARE(exynos5260_clk_fsys, "samsung,exynos5260-clock-fsys", @@ -576,21 +576,21 @@ static const struct samsung_gate_clock g2d_gate_clks[] __initconst = { EN_IP_G2D_SECURE_SMMU_G2D, 15, 0, 0), }; +static const struct samsung_cmu_info g2d_cmu __initconst = { + .mux_clks = g2d_mux_clks, + .nr_mux_clks = ARRAY_SIZE(g2d_mux_clks), + .div_clks = g2d_div_clks, + .nr_div_clks = ARRAY_SIZE(g2d_div_clks), + .gate_clks = g2d_gate_clks, + .nr_gate_clks = ARRAY_SIZE(g2d_gate_clks), + .nr_clk_ids = G2D_NR_CLK, + .clk_regs = g2d_clk_regs, + .nr_clk_regs = ARRAY_SIZE(g2d_clk_regs), +}; + static void __init exynos5260_clk_g2d_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = g2d_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks); - cmu.div_clks = g2d_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(g2d_div_clks); - cmu.gate_clks = g2d_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(g2d_gate_clks); - cmu.nr_clk_ids = G2D_NR_CLK; - cmu.clk_regs = g2d_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(g2d_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &g2d_cmu); } CLK_OF_DECLARE(exynos5260_clk_g2d, "samsung,exynos5260-clock-g2d", @@ -637,23 +637,23 @@ static const struct samsung_pll_clock g3d_pll_clks[] __initconst = { pll2550_24mhz_tbl), }; +static const struct samsung_cmu_info g3d_cmu __initconst = { + .pll_clks = g3d_pll_clks, + .nr_pll_clks = ARRAY_SIZE(g3d_pll_clks), + .mux_clks = g3d_mux_clks, + .nr_mux_clks = ARRAY_SIZE(g3d_mux_clks), + .div_clks = g3d_div_clks, + .nr_div_clks = ARRAY_SIZE(g3d_div_clks), + .gate_clks = g3d_gate_clks, + .nr_gate_clks = ARRAY_SIZE(g3d_gate_clks), + .nr_clk_ids = G3D_NR_CLK, + .clk_regs = g3d_clk_regs, + .nr_clk_regs = ARRAY_SIZE(g3d_clk_regs), +}; + static void __init exynos5260_clk_g3d_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.pll_clks = g3d_pll_clks; - cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks); - cmu.mux_clks = g3d_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(g3d_mux_clks); - cmu.div_clks = g3d_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(g3d_div_clks); - cmu.gate_clks = g3d_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(g3d_gate_clks); - cmu.nr_clk_ids = G3D_NR_CLK; - cmu.clk_regs = g3d_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(g3d_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &g3d_cmu); } CLK_OF_DECLARE(exynos5260_clk_g3d, "samsung,exynos5260-clock-g3d", @@ -772,21 +772,21 @@ static const struct samsung_gate_clock gscl_gate_clks[] __initconst = { EN_IP_GSCL_SECURE_SMMU_MSCL1, 20, 0, 0), }; +static const struct samsung_cmu_info gscl_cmu __initconst = { + .mux_clks = gscl_mux_clks, + .nr_mux_clks = ARRAY_SIZE(gscl_mux_clks), + .div_clks = gscl_div_clks, + .nr_div_clks = ARRAY_SIZE(gscl_div_clks), + .gate_clks = gscl_gate_clks, + .nr_gate_clks = ARRAY_SIZE(gscl_gate_clks), + .nr_clk_ids = GSCL_NR_CLK, + .clk_regs = gscl_clk_regs, + .nr_clk_regs = ARRAY_SIZE(gscl_clk_regs), +}; + static void __init exynos5260_clk_gscl_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = gscl_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks); - cmu.div_clks = gscl_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(gscl_div_clks); - cmu.gate_clks = gscl_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(gscl_gate_clks); - cmu.nr_clk_ids = GSCL_NR_CLK; - cmu.clk_regs = gscl_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(gscl_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &gscl_cmu); } CLK_OF_DECLARE(exynos5260_clk_gscl, "samsung,exynos5260-clock-gscl", @@ -891,21 +891,21 @@ static const struct samsung_gate_clock isp_gate_clks[] __initconst = { EN_SCLK_ISP, 9, CLK_SET_RATE_PARENT, 0), }; +static const struct samsung_cmu_info isp_cmu __initconst = { + .mux_clks = isp_mux_clks, + .nr_mux_clks = ARRAY_SIZE(isp_mux_clks), + .div_clks = isp_div_clks, + .nr_div_clks = ARRAY_SIZE(isp_div_clks), + .gate_clks = isp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(isp_gate_clks), + .nr_clk_ids = ISP_NR_CLK, + .clk_regs = isp_clk_regs, + .nr_clk_regs = ARRAY_SIZE(isp_clk_regs), +}; + static void __init exynos5260_clk_isp_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = isp_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks); - cmu.div_clks = isp_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(isp_div_clks); - cmu.gate_clks = isp_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(isp_gate_clks); - cmu.nr_clk_ids = ISP_NR_CLK; - cmu.clk_regs = isp_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(isp_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &isp_cmu); } CLK_OF_DECLARE(exynos5260_clk_isp, "samsung,exynos5260-clock-isp", @@ -955,21 +955,21 @@ static const struct samsung_pll_clock kfc_pll_clks[] __initconst = { pll2550_24mhz_tbl), }; +static const struct samsung_cmu_info kfc_cmu __initconst = { + .pll_clks = kfc_pll_clks, + .nr_pll_clks = ARRAY_SIZE(kfc_pll_clks), + .mux_clks = kfc_mux_clks, + .nr_mux_clks = ARRAY_SIZE(kfc_mux_clks), + .div_clks = kfc_div_clks, + .nr_div_clks = ARRAY_SIZE(kfc_div_clks), + .nr_clk_ids = KFC_NR_CLK, + .clk_regs = kfc_clk_regs, + .nr_clk_regs = ARRAY_SIZE(kfc_clk_regs), +}; + static void __init exynos5260_clk_kfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.pll_clks = kfc_pll_clks; - cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks); - cmu.mux_clks = kfc_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(kfc_mux_clks); - cmu.div_clks = kfc_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(kfc_div_clks); - cmu.nr_clk_ids = KFC_NR_CLK; - cmu.clk_regs = kfc_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(kfc_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &kfc_cmu); } CLK_OF_DECLARE(exynos5260_clk_kfc, "samsung,exynos5260-clock-kfc", @@ -1011,21 +1011,21 @@ static const struct samsung_gate_clock mfc_gate_clks[] __initconst = { EN_IP_MFC_SECURE_SMMU2_MFC, 7, 0, 0), }; +static const struct samsung_cmu_info mfc_cmu __initconst = { + .mux_clks = mfc_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mfc_mux_clks), + .div_clks = mfc_div_clks, + .nr_div_clks = ARRAY_SIZE(mfc_div_clks), + .gate_clks = mfc_gate_clks, + .nr_gate_clks = ARRAY_SIZE(mfc_gate_clks), + .nr_clk_ids = MFC_NR_CLK, + .clk_regs = mfc_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mfc_clk_regs), +}; + static void __init exynos5260_clk_mfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = mfc_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks); - cmu.div_clks = mfc_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(mfc_div_clks); - cmu.gate_clks = mfc_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(mfc_gate_clks); - cmu.nr_clk_ids = MFC_NR_CLK; - cmu.clk_regs = mfc_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(mfc_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &mfc_cmu); } CLK_OF_DECLARE(exynos5260_clk_mfc, "samsung,exynos5260-clock-mfc", @@ -1158,23 +1158,23 @@ static const struct samsung_pll_clock mif_pll_clks[] __initconst = { pll2550_24mhz_tbl), }; +static const struct samsung_cmu_info mif_cmu __initconst = { + .pll_clks = mif_pll_clks, + .nr_pll_clks = ARRAY_SIZE(mif_pll_clks), + .mux_clks = mif_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mif_mux_clks), + .div_clks = mif_div_clks, + .nr_div_clks = ARRAY_SIZE(mif_div_clks), + .gate_clks = mif_gate_clks, + .nr_gate_clks = ARRAY_SIZE(mif_gate_clks), + .nr_clk_ids = MIF_NR_CLK, + .clk_regs = mif_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mif_clk_regs), +}; + static void __init exynos5260_clk_mif_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.pll_clks = mif_pll_clks; - cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks); - cmu.mux_clks = mif_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(mif_mux_clks); - cmu.div_clks = mif_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(mif_div_clks); - cmu.gate_clks = mif_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(mif_gate_clks); - cmu.nr_clk_ids = MIF_NR_CLK; - cmu.clk_regs = mif_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(mif_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &mif_cmu); } CLK_OF_DECLARE(exynos5260_clk_mif, "samsung,exynos5260-clock-mif", @@ -1366,21 +1366,21 @@ static const struct samsung_gate_clock peri_gate_clks[] __initconst = { EN_IP_PERI_SECURE_TZPC, 20, 0, 0), }; +static const struct samsung_cmu_info peri_cmu __initconst = { + .mux_clks = peri_mux_clks, + .nr_mux_clks = ARRAY_SIZE(peri_mux_clks), + .div_clks = peri_div_clks, + .nr_div_clks = ARRAY_SIZE(peri_div_clks), + .gate_clks = peri_gate_clks, + .nr_gate_clks = ARRAY_SIZE(peri_gate_clks), + .nr_clk_ids = PERI_NR_CLK, + .clk_regs = peri_clk_regs, + .nr_clk_regs = ARRAY_SIZE(peri_clk_regs), +}; + static void __init exynos5260_clk_peri_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.mux_clks = peri_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks); - cmu.div_clks = peri_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(peri_div_clks); - cmu.gate_clks = peri_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(peri_gate_clks); - cmu.nr_clk_ids = PERI_NR_CLK; - cmu.clk_regs = peri_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(peri_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &peri_cmu); } CLK_OF_DECLARE(exynos5260_clk_peri, "samsung,exynos5260-clock-peri", @@ -1818,25 +1818,25 @@ static const struct samsung_pll_clock top_pll_clks[] __initconst = { pll2650_24mhz_tbl), }; +static const struct samsung_cmu_info top_cmu __initconst = { + .pll_clks = top_pll_clks, + .nr_pll_clks = ARRAY_SIZE(top_pll_clks), + .mux_clks = top_mux_clks, + .nr_mux_clks = ARRAY_SIZE(top_mux_clks), + .div_clks = top_div_clks, + .nr_div_clks = ARRAY_SIZE(top_div_clks), + .gate_clks = top_gate_clks, + .nr_gate_clks = ARRAY_SIZE(top_gate_clks), + .fixed_clks = fixed_rate_clks, + .nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks), + .nr_clk_ids = TOP_NR_CLK, + .clk_regs = top_clk_regs, + .nr_clk_regs = ARRAY_SIZE(top_clk_regs), +}; + static void __init exynos5260_clk_top_init(struct device_node *np) { - struct samsung_cmu_info cmu = { NULL }; - - cmu.pll_clks = top_pll_clks; - cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks); - cmu.mux_clks = top_mux_clks; - cmu.nr_mux_clks = ARRAY_SIZE(top_mux_clks); - cmu.div_clks = top_div_clks; - cmu.nr_div_clks = ARRAY_SIZE(top_div_clks); - cmu.gate_clks = top_gate_clks; - cmu.nr_gate_clks = ARRAY_SIZE(top_gate_clks); - cmu.fixed_clks = fixed_rate_clks; - cmu.nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks); - cmu.nr_clk_ids = TOP_NR_CLK; - cmu.clk_regs = top_clk_regs; - cmu.nr_clk_regs = ARRAY_SIZE(top_clk_regs); - - samsung_cmu_register_one(np, &cmu); + samsung_cmu_register_one(np, &top_cmu); } CLK_OF_DECLARE(exynos5260_clk_top, "samsung,exynos5260-clock-top", diff --git a/drivers/clk/samsung/clk-exynos5410.c b/drivers/clk/samsung/clk-exynos5410.c index 54ec486..fc471a4 100644 --- a/drivers/clk/samsung/clk-exynos5410.c +++ b/drivers/clk/samsung/clk-exynos5410.c @@ -14,6 +14,7 @@ #include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/clk.h> #include "clk.h" @@ -21,6 +22,8 @@ #define APLL_CON0 0x100 #define CPLL_LOCK 0x10020 #define CPLL_CON0 0x10120 +#define EPLL_LOCK 0x10040 +#define EPLL_CON0 0x10130 #define MPLL_LOCK 0x4000 #define MPLL_CON0 0x4100 #define BPLL_LOCK 0x20010 @@ -58,7 +61,7 @@ /* list of PLLs */ enum exynos5410_plls { - apll, cpll, mpll, + apll, cpll, epll, mpll, bpll, kpll, nr_plls /* number of PLLs */ }; @@ -67,6 +70,7 @@ enum exynos5410_plls { PNAME(apll_p) = { "fin_pll", "fout_apll", }; PNAME(bpll_p) = { "fin_pll", "fout_bpll", }; PNAME(cpll_p) = { "fin_pll", "fout_cpll" }; +PNAME(epll_p) = { "fin_pll", "fout_epll" }; PNAME(mpll_p) = { "fin_pll", "fout_mpll", }; PNAME(kpll_p) = { "fin_pll", "fout_kpll", }; @@ -95,6 +99,8 @@ static const struct samsung_mux_clock exynos5410_mux_clks[] __initconst = { MUX(0, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1), MUX(0, "sclk_bpll_muxed", bpll_user_p, SRC_TOP2, 24, 1), + MUX(0, "sclk_epll", epll_p, SRC_TOP2, 12, 1), + MUX(0, "sclk_cpll", cpll_p, SRC_TOP2, 8, 1), MUX(0, "sclk_mpll_bpll", mpll_bpll_p, SRC_TOP1, 20, 1), @@ -176,6 +182,8 @@ static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = { GATE(CLK_MMC0, "sdmmc0", "aclk200", GATE_BUS_FSYS0, 12, 0, 0), GATE(CLK_MMC1, "sdmmc1", "aclk200", GATE_BUS_FSYS0, 13, 0, 0), GATE(CLK_MMC2, "sdmmc2", "aclk200", GATE_BUS_FSYS0, 14, 0, 0), + GATE(CLK_PDMA1, "pdma1", "aclk200", GATE_BUS_FSYS0, 2, 0, 0), + GATE(CLK_PDMA0, "pdma0", "aclk200", GATE_BUS_FSYS0, 1, 0, 0), GATE(CLK_SCLK_USBPHY301, "sclk_usbphy301", "dout_usbphy301", GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0), @@ -217,11 +225,26 @@ static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = { GATE(CLK_USBD301, "usbd301", "aclk200_fsys", GATE_IP_FSYS, 20, 0, 0), }; -static const struct samsung_pll_clock exynos5410_plls[nr_plls] __initconst = { +static const struct samsung_pll_rate_table exynos5410_pll2550x_24mhz_tbl[] __initconst = { + PLL_36XX_RATE(400000000U, 200, 3, 2, 0), + PLL_36XX_RATE(333000000U, 111, 2, 2, 0), + PLL_36XX_RATE(300000000U, 100, 2, 2, 0), + PLL_36XX_RATE(266000000U, 266, 3, 3, 0), + PLL_36XX_RATE(200000000U, 200, 3, 3, 0), + PLL_36XX_RATE(192000000U, 192, 3, 3, 0), + PLL_36XX_RATE(166000000U, 166, 3, 3, 0), + PLL_36XX_RATE(133000000U, 266, 3, 4, 0), + PLL_36XX_RATE(100000000U, 200, 3, 4, 0), + PLL_36XX_RATE(66000000U, 176, 2, 5, 0), +}; + +static struct samsung_pll_clock exynos5410_plls[nr_plls] __initdata = { [apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK, CPLL_CON0, NULL), + [epll] = PLL(pll_2650x, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, NULL), [mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK, MPLL_CON0, NULL), [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK, @@ -230,29 +253,27 @@ static const struct samsung_pll_clock exynos5410_plls[nr_plls] __initconst = { KPLL_CON0, NULL), }; +static const struct samsung_cmu_info cmu __initconst = { + .pll_clks = exynos5410_plls, + .nr_pll_clks = ARRAY_SIZE(exynos5410_plls), + .mux_clks = exynos5410_mux_clks, + .nr_mux_clks = ARRAY_SIZE(exynos5410_mux_clks), + .div_clks = exynos5410_div_clks, + .nr_div_clks = ARRAY_SIZE(exynos5410_div_clks), + .gate_clks = exynos5410_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5410_gate_clks), + .nr_clk_ids = CLK_NR_CLKS, +}; + /* register exynos5410 clocks */ static void __init exynos5410_clk_init(struct device_node *np) { - struct samsung_clk_provider *ctx; - void __iomem *reg_base; - - reg_base = of_iomap(np, 0); - if (!reg_base) - panic("%s: failed to map registers\n", __func__); - - ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS); - - samsung_clk_register_pll(ctx, exynos5410_plls, - ARRAY_SIZE(exynos5410_plls), reg_base); + struct clk *xxti = of_clk_get(np, 0); - samsung_clk_register_mux(ctx, exynos5410_mux_clks, - ARRAY_SIZE(exynos5410_mux_clks)); - samsung_clk_register_div(ctx, exynos5410_div_clks, - ARRAY_SIZE(exynos5410_div_clks)); - samsung_clk_register_gate(ctx, exynos5410_gate_clks, - ARRAY_SIZE(exynos5410_gate_clks)); + if (!IS_ERR(xxti) && clk_get_rate(xxti) == 24 * MHZ) + exynos5410_plls[epll].rate_table = exynos5410_pll2550x_24mhz_tbl; - samsung_clk_of_add_provider(np, ctx); + samsung_cmu_register_one(np, &cmu); pr_debug("Exynos5410: clock setup completed.\n"); } diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index bb196ca..8c8b495 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -131,6 +131,9 @@ #define TOP_SPARE2 0x10b08 #define BPLL_LOCK 0x20010 #define BPLL_CON0 0x20110 +#define SRC_CDREX 0x20200 +#define DIV_CDREX0 0x20500 +#define DIV_CDREX1 0x20504 #define KPLL_LOCK 0x28000 #define KPLL_CON0 0x28100 #define SRC_KFC 0x28200 @@ -244,6 +247,9 @@ static const unsigned long exynos5x_clk_regs[] __initconst = { GATE_TOP_SCLK_FSYS, GATE_TOP_SCLK_PERIC, TOP_SPARE2, + SRC_CDREX, + DIV_CDREX0, + DIV_CDREX1, SRC_KFC, DIV_KFC0, }; @@ -448,6 +454,8 @@ PNAME(mout_maudio0_p) = {"fin_pll", "maudio_clk", "mout_sclk_dpll", "mout_sclk_epll", "mout_sclk_rpll"}; PNAME(mout_mau_epll_clk_p) = {"mout_sclk_epll", "mout_sclk_dpll", "mout_sclk_mpll", "mout_sclk_spll"}; +PNAME(mout_mclk_cdrex_p) = {"mout_bpll", "mout_mx_mspll_ccore"}; + /* List of parents specific to exynos5800 */ PNAME(mout_epll2_5800_p) = { "mout_sclk_epll", "ff_dout_epll2" }; PNAME(mout_group1_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", @@ -465,6 +473,9 @@ PNAME(mout_group6_5800_p) = { "mout_sclk_ipll", "mout_sclk_dpll", PNAME(mout_group7_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", "mout_sclk_mpll", "mout_sclk_spll", "mout_epll2", "mout_sclk_ipll" }; +PNAME(mout_mx_mspll_ccore_p) = {"sclk_bpll", "mout_sclk_dpll", + "mout_sclk_mpll", "ff_dout_spll2", + "mout_sclk_spll", "mout_sclk_epll"}; PNAME(mout_mau_epll_clk_5800_p) = { "mout_sclk_epll", "mout_sclk_dpll", "mout_sclk_mpll", "ff_dout_spll2" }; @@ -523,6 +534,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = { MUX(0, "mout_aclk300_disp1", mout_group5_5800_p, SRC_TOP2, 24, 2), MUX(0, "mout_aclk300_gscl", mout_group5_5800_p, SRC_TOP2, 28, 2), + MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore", + mout_mx_mspll_ccore_p, SRC_TOP7, 16, 2), MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, SRC_TOP7, 20, 2), MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1), @@ -601,6 +614,8 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = { MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2), MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2), + MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore", + mout_group5_5800_p, SRC_TOP7, 16, 2), MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2), MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), @@ -744,6 +759,12 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_fimd1_final", mout_fimd1_final_p, TOP_SPARE2, 8, 1), + /* CDREX block */ + MUX_F(CLK_MOUT_MCLK_CDREX, "mout_mclk_cdrex", mout_mclk_cdrex_p, + SRC_CDREX, 4, 1, CLK_SET_RATE_PARENT, 0), + MUX_F(CLK_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_CDREX, 0, 1, + CLK_SET_RATE_PARENT, 0), + /* MAU Block */ MUX(CLK_MOUT_MAUDIO0, "mout_maudio0", mout_maudio0_p, SRC_MAU, 28, 3), @@ -836,6 +857,21 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = { DIV(CLK_DOUT_ACLK400_DISP1, "dout_aclk400_disp1", "mout_aclk400_disp1", DIV_TOP2, 4, 3), + /* CDREX Block */ + DIV(CLK_DOUT_PCLK_CDREX, "dout_pclk_cdrex", "dout_aclk_cdrex1", + DIV_CDREX0, 28, 3), + DIV_F(CLK_DOUT_SCLK_CDREX, "dout_sclk_cdrex", "mout_mclk_cdrex", + DIV_CDREX0, 24, 3, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DOUT_ACLK_CDREX1, "dout_aclk_cdrex1", "dout_clk2x_phy0", + DIV_CDREX0, 16, 3), + DIV(CLK_DOUT_CCLK_DREX0, "dout_cclk_drex0", "dout_clk2x_phy0", + DIV_CDREX0, 8, 3), + DIV(CLK_DOUT_CLK2X_PHY0, "dout_clk2x_phy0", "dout_sclk_cdrex", + DIV_CDREX0, 3, 5), + + DIV(CLK_DOUT_PCLK_CORE_MEM, "dout_pclk_core_mem", "mout_mclk_cdrex", + DIV_CDREX1, 8, 3), + /* Audio Block */ DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4), DIV(0, "dout_maupcm0", "dout_maudio0", DIV_MAU, 24, 8), @@ -1364,6 +1400,7 @@ static void __init exynos5x_clk_init(struct device_node *np, if (_get_rate("fin_pll") == 24 * MHZ) { exynos5x_plls[apll].rate_table = exynos5420_pll2550x_24mhz_tbl; exynos5x_plls[kpll].rate_table = exynos5420_pll2550x_24mhz_tbl; + exynos5x_plls[bpll].rate_table = exynos5420_pll2550x_24mhz_tbl; } samsung_clk_register_pll(ctx, exynos5x_plls, ARRAY_SIZE(exynos5x_plls), diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index a57d01b..a80f3ef 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -112,6 +112,11 @@ static struct notifier_block exynos5440_clk_restart_handler = { .priority = 128, }; +static const struct samsung_pll_clock exynos5440_plls[] __initconst = { + PLL(pll_2550x, CLK_CPLLA, "cplla", "xtal", 0, 0x4c, NULL), + PLL(pll_2550x, CLK_CPLLB, "cpllb", "xtal", 0, 0x50, NULL), +}; + /* register exynos5440 clocks */ static void __init exynos5440_clk_init(struct device_node *np) { @@ -129,8 +134,8 @@ static void __init exynos5440_clk_init(struct device_node *np) samsung_clk_of_register_fixed_ext(ctx, exynos5440_fixed_rate_ext_clks, ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); - samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10); - samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10); + samsung_clk_register_pll(ctx, exynos5440_plls, + ARRAY_SIZE(exynos5440_plls), ctx->reg_base); samsung_clk_register_fixed_rate(ctx, exynos5440_fixed_rate_clks, ARRAY_SIZE(exynos5440_fixed_rate_clks)); diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 48139bd..9617825 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -890,22 +890,14 @@ static const struct clk_ops samsung_s3c2440_mpll_clk_ops = { #define PLL2550X_M_SHIFT (4) #define PLL2550X_S_SHIFT (0) -struct samsung_clk_pll2550x { - struct clk_hw hw; - const void __iomem *reg_base; - unsigned long offset; -}; - -#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw) - static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 r, p, m, s, pll_stat; u64 fvco = parent_rate; - pll_stat = readl_relaxed(pll->reg_base + pll->offset * 3); + pll_stat = readl_relaxed(pll->con_reg); r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK; if (!r) return 0; @@ -923,43 +915,6 @@ static const struct clk_ops samsung_pll2550x_clk_ops = { .recalc_rate = samsung_pll2550x_recalc_rate, }; -struct clk * __init samsung_clk_register_pll2550x(const char *name, - const char *pname, const void __iomem *reg_base, - const unsigned long offset) -{ - struct samsung_clk_pll2550x *pll; - struct clk *clk; - struct clk_init_data init; - - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; - } - - init.name = name; - init.ops = &samsung_pll2550x_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; - - pll->hw.init = &init; - pll->reg_base = reg_base; - pll->offset = offset; - - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) { - pr_err("%s: failed to register pll clock %s\n", __func__, - name); - kfree(pll); - } - - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); - - return clk; -} - /* * PLL2550xx Clock Type */ @@ -1063,6 +1018,102 @@ static const struct clk_ops samsung_pll2550xx_clk_min_ops = { }; /* + * PLL2650x Clock Type + */ + +/* Maximum lock time can be 3000 * PDIV cycles */ +#define PLL2650X_LOCK_FACTOR 3000 + +#define PLL2650X_M_MASK 0x1ff +#define PLL2650X_P_MASK 0x3f +#define PLL2650X_S_MASK 0x7 +#define PLL2650X_K_MASK 0xffff +#define PLL2650X_LOCK_STAT_MASK 0x1 +#define PLL2650X_M_SHIFT 16 +#define PLL2650X_P_SHIFT 8 +#define PLL2650X_S_SHIFT 0 +#define PLL2650X_K_SHIFT 0 +#define PLL2650X_LOCK_STAT_SHIFT 29 +#define PLL2650X_PLL_ENABLE_SHIFT 31 + +static unsigned long samsung_pll2650x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u64 fout = parent_rate; + u32 mdiv, pdiv, sdiv, pll_con0, pll_con1; + s16 kdiv; + + pll_con0 = readl_relaxed(pll->con_reg); + mdiv = (pll_con0 >> PLL2650X_M_SHIFT) & PLL2650X_M_MASK; + pdiv = (pll_con0 >> PLL2650X_P_SHIFT) & PLL2650X_P_MASK; + sdiv = (pll_con0 >> PLL2650X_S_SHIFT) & PLL2650X_S_MASK; + + pll_con1 = readl_relaxed(pll->con_reg + 4); + kdiv = (s16)((pll_con1 >> PLL2650X_K_SHIFT) & PLL2650X_K_MASK); + + fout *= (mdiv << 16) + kdiv; + do_div(fout, (pdiv << sdiv)); + fout >>= 16; + + return (unsigned long)fout; +} + +static int samsung_pll2650x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 con0, con1; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } + + con0 = readl_relaxed(pll->con_reg); + con1 = readl_relaxed(pll->con_reg + 4); + + /* Set PLL lock time. */ + writel_relaxed(rate->pdiv * PLL2650X_LOCK_FACTOR, pll->lock_reg); + + /* Change PLL PMS values */ + con0 &= ~((PLL2650X_M_MASK << PLL2650X_M_SHIFT) | + (PLL2650X_P_MASK << PLL2650X_P_SHIFT) | + (PLL2650X_S_MASK << PLL2650X_S_SHIFT)); + con0 |= (rate->mdiv << PLL2650X_M_SHIFT) | + (rate->pdiv << PLL2650X_P_SHIFT) | + (rate->sdiv << PLL2650X_S_SHIFT); + con0 |= (1 << PLL2650X_PLL_ENABLE_SHIFT); + writel_relaxed(con0, pll->con_reg); + + con1 &= ~(PLL2650X_K_MASK << PLL2650X_K_SHIFT); + con1 |= ((rate->kdiv & PLL2650X_K_MASK) << PLL2650X_K_SHIFT); + writel_relaxed(con1, pll->con_reg + 4); + + do { + cpu_relax(); + con0 = readl_relaxed(pll->con_reg); + } while (!(con0 & (PLL2650X_LOCK_STAT_MASK + << PLL2650X_LOCK_STAT_SHIFT))); + + return 0; +} + +static const struct clk_ops samsung_pll2650x_clk_ops = { + .recalc_rate = samsung_pll2650x_recalc_rate, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_pll2650x_set_rate, +}; + +static const struct clk_ops samsung_pll2650x_clk_min_ops = { + .recalc_rate = samsung_pll2650x_recalc_rate, +}; + +/* * PLL2650XX Clock Type */ @@ -1263,12 +1314,21 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, else init.ops = &samsung_s3c2440_mpll_clk_ops; break; + case pll_2550x: + init.ops = &samsung_pll2550x_clk_ops; + break; case pll_2550xx: if (!pll->rate_table) init.ops = &samsung_pll2550xx_clk_min_ops; else init.ops = &samsung_pll2550xx_clk_ops; break; + case pll_2650x: + if (!pll->rate_table) + init.ops = &samsung_pll2650x_clk_min_ops; + else + init.ops = &samsung_pll2650x_clk_ops; + break; case pll_2650xx: if (!pll->rate_table) init.ops = &samsung_pll2650xx_clk_min_ops; diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index 213de9a..a1ca023 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -31,7 +31,9 @@ enum samsung_pll_type { pll_s3c2410_mpll, pll_s3c2410_upll, pll_s3c2440_mpll, + pll_2550x, pll_2550xx, + pll_2650x, pll_2650xx, pll_1450x, pll_1451x, diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index 546bd79..a485f3b 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -15,6 +15,11 @@ #include <linux/of.h> #include <linux/of_address.h> +struct clkgen_data { + unsigned long flags; + bool mode; +}; + struct flexgen { struct clk_hw hw; @@ -28,9 +33,14 @@ struct flexgen { struct clk_gate fgate; /* Final divisor */ struct clk_divider fdiv; + /* Asynchronous mode control */ + struct clk_gate sync; + /* hw control flags */ + bool control_mode; }; #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw) +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) static int flexgen_enable(struct clk_hw *hw) { @@ -139,12 +149,21 @@ static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate, struct flexgen *flexgen = to_flexgen(hw); struct clk_hw *pdiv_hw = &flexgen->pdiv.hw; struct clk_hw *fdiv_hw = &flexgen->fdiv.hw; + struct clk_hw *sync_hw = &flexgen->sync.hw; + struct clk_gate *config = to_clk_gate(sync_hw); unsigned long div = 0; int ret = 0; + u32 reg; __clk_hw_set_clk(pdiv_hw, hw); __clk_hw_set_clk(fdiv_hw, hw); + if (flexgen->control_mode) { + reg = readl(config->reg); + reg &= ~BIT(config->bit_idx); + writel(reg, config->reg); + } + div = clk_best_div(parent_rate, rate); /* @@ -178,7 +197,7 @@ static const struct clk_ops flexgen_ops = { static struct clk *clk_register_flexgen(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, spinlock_t *lock, u32 idx, - unsigned long flexgen_flags) { + unsigned long flexgen_flags, bool mode) { struct flexgen *fgxbar; struct clk *clk; struct clk_init_data init; @@ -227,6 +246,13 @@ static struct clk *clk_register_flexgen(const char *name, fgxbar->fdiv.reg = fdiv_reg; fgxbar->fdiv.width = 6; + /* Final divider sync config */ + fgxbar->sync.lock = lock; + fgxbar->sync.reg = fdiv_reg; + fgxbar->sync.bit_idx = 7; + + fgxbar->control_mode = mode; + fgxbar->hw.init = &init; clk = clk_register(NULL, &fgxbar->hw); @@ -259,6 +285,27 @@ static const char ** __init flexgen_get_parents(struct device_node *np, return parents; } +static const struct clkgen_data clkgen_audio = { + .flags = CLK_SET_RATE_PARENT, +}; + +static const struct clkgen_data clkgen_video = { + .flags = CLK_SET_RATE_PARENT, + .mode = 1, +}; + +static const struct of_device_id flexgen_of_match[] = { + { + .compatible = "st,flexgen-audio", + .data = &clkgen_audio, + }, + { + .compatible = "st,flexgen-video", + .data = &clkgen_video, + }, + {} +}; + static void __init st_of_flexgen_setup(struct device_node *np) { struct device_node *pnode; @@ -267,7 +314,11 @@ static void __init st_of_flexgen_setup(struct device_node *np) const char **parents; int num_parents, i; spinlock_t *rlock = NULL; + const struct of_device_id *match; + struct clkgen_data *data = NULL; + unsigned long flex_flags = 0; int ret; + bool clk_mode = 0; pnode = of_get_parent(np); if (!pnode) @@ -281,6 +332,13 @@ static void __init st_of_flexgen_setup(struct device_node *np) if (!parents) return; + match = of_match_node(flexgen_of_match, np); + if (match) { + data = (struct clkgen_data *)match->data; + flex_flags = data->flags; + clk_mode = data->mode; + } + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); if (!clk_data) goto err; @@ -307,7 +365,6 @@ static void __init st_of_flexgen_setup(struct device_node *np) for (i = 0; i < clk_data->clk_num; i++) { struct clk *clk; const char *clk_name; - unsigned long flex_flags = 0; if (of_property_read_string_index(np, "clock-output-names", i, &clk_name)) { @@ -323,7 +380,7 @@ static void __init st_of_flexgen_setup(struct device_node *np) continue; clk = clk_register_flexgen(clk_name, parents, num_parents, - reg, rlock, i, flex_flags); + reg, rlock, i, flex_flags, clk_mode); if (IS_ERR(clk)) goto err; diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index 09afeb8..14819d9 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -42,79 +42,6 @@ struct stm_fs { unsigned long nsdiv; }; -static const struct stm_fs fs216c65_rtbl[] = { - { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 312.5 Khz */ - { .mdiv = 0x17, .pe = 0x25ed, .sdiv = 0x1, .nsdiv = 0 }, /* 27 MHz */ - { .mdiv = 0x1a, .pe = 0x7b36, .sdiv = 0x2, .nsdiv = 1 }, /* 36.87 MHz */ - { .mdiv = 0x13, .pe = 0x0, .sdiv = 0x2, .nsdiv = 1 }, /* 48 MHz */ - { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x1, .nsdiv = 1 }, /* 108 MHz */ -}; - -static const struct stm_fs fs432c65_rtbl[] = { - { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 625 Khz */ - { .mdiv = 0x13, .pe = 0x777c, .sdiv = 0x4, .nsdiv = 1 }, /* 25.175 MHz */ - { .mdiv = 0x19, .pe = 0x4d35, .sdiv = 0x2, .nsdiv = 0 }, /* 25.200 MHz */ - { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x4, .nsdiv = 1 }, /* 27.000 MHz */ - { .mdiv = 0x17, .pe = 0x28f5, .sdiv = 0x2, .nsdiv = 0 }, /* 27.027 MHz */ - { .mdiv = 0x16, .pe = 0x3359, .sdiv = 0x2, .nsdiv = 0 }, /* 28.320 MHz */ - { .mdiv = 0x1f, .pe = 0x2083, .sdiv = 0x3, .nsdiv = 1 }, /* 30.240 MHz */ - { .mdiv = 0x1e, .pe = 0x430d, .sdiv = 0x3, .nsdiv = 1 }, /* 31.500 MHz */ - { .mdiv = 0x17, .pe = 0x0, .sdiv = 0x3, .nsdiv = 1 }, /* 40.000 MHz */ - { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x1, .nsdiv = 0 }, /* 49.500 MHz */ - { .mdiv = 0x13, .pe = 0x6667, .sdiv = 0x3, .nsdiv = 1 }, /* 50.000 MHz */ - { .mdiv = 0x10, .pe = 0x1ee6, .sdiv = 0x3, .nsdiv = 1 }, /* 57.284 MHz */ - { .mdiv = 0x1d, .pe = 0x3b14, .sdiv = 0x2, .nsdiv = 1 }, /* 65.000 MHz */ - { .mdiv = 0x12, .pe = 0x7c65, .sdiv = 0x1, .nsdiv = 0 }, /* 71.000 MHz */ - { .mdiv = 0x19, .pe = 0xecd, .sdiv = 0x2, .nsdiv = 1 }, /* 74.176 MHz */ - { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x2, .nsdiv = 1 }, /* 74.250 MHz */ - { .mdiv = 0x19, .pe = 0x3334, .sdiv = 0x2, .nsdiv = 1 }, /* 75.000 MHz */ - { .mdiv = 0x18, .pe = 0x5138, .sdiv = 0x2, .nsdiv = 1 }, /* 78.800 MHz */ - { .mdiv = 0x1d, .pe = 0x77d, .sdiv = 0x0, .nsdiv = 0 }, /* 85.500 MHz */ - { .mdiv = 0x1c, .pe = 0x13d5, .sdiv = 0x0, .nsdiv = 0 }, /* 88.750 MHz */ - { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x2, .nsdiv = 1 }, /* 108.000 MHz */ - { .mdiv = 0x17, .pe = 0x28f5, .sdiv = 0x0, .nsdiv = 0 }, /* 108.108 MHz */ - { .mdiv = 0x10, .pe = 0x6e26, .sdiv = 0x2, .nsdiv = 1 }, /* 118.963 MHz */ - { .mdiv = 0x15, .pe = 0x3e63, .sdiv = 0x0, .nsdiv = 0 }, /* 119.000 MHz */ - { .mdiv = 0x1c, .pe = 0x471d, .sdiv = 0x1, .nsdiv = 1 }, /* 135.000 MHz */ - { .mdiv = 0x19, .pe = 0xecd, .sdiv = 0x1, .nsdiv = 1 }, /* 148.352 MHz */ - { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x1, .nsdiv = 1 }, /* 148.500 MHz */ - { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x0, .nsdiv = 1 }, /* 297 MHz */ -}; - -static const struct stm_fs fs660c32_rtbl[] = { - { .mdiv = 0x14, .pe = 0x376b, .sdiv = 0x4, .nsdiv = 1 }, /* 25.175 MHz */ - { .mdiv = 0x14, .pe = 0x30c3, .sdiv = 0x4, .nsdiv = 1 }, /* 25.200 MHz */ - { .mdiv = 0x10, .pe = 0x71c7, .sdiv = 0x4, .nsdiv = 1 }, /* 27.000 MHz */ - { .mdiv = 0x00, .pe = 0x47af, .sdiv = 0x3, .nsdiv = 0 }, /* 27.027 MHz */ - { .mdiv = 0x0e, .pe = 0x4e1a, .sdiv = 0x4, .nsdiv = 1 }, /* 28.320 MHz */ - { .mdiv = 0x0b, .pe = 0x534d, .sdiv = 0x4, .nsdiv = 1 }, /* 30.240 MHz */ - { .mdiv = 0x17, .pe = 0x6fbf, .sdiv = 0x2, .nsdiv = 0 }, /* 31.500 MHz */ - { .mdiv = 0x01, .pe = 0x0, .sdiv = 0x4, .nsdiv = 1 }, /* 40.000 MHz */ - { .mdiv = 0x15, .pe = 0x2aab, .sdiv = 0x3, .nsdiv = 1 }, /* 49.500 MHz */ - { .mdiv = 0x14, .pe = 0x6666, .sdiv = 0x3, .nsdiv = 1 }, /* 50.000 MHz */ - { .mdiv = 0x1d, .pe = 0x395f, .sdiv = 0x1, .nsdiv = 0 }, /* 57.284 MHz */ - { .mdiv = 0x08, .pe = 0x4ec5, .sdiv = 0x3, .nsdiv = 1 }, /* 65.000 MHz */ - { .mdiv = 0x05, .pe = 0x1770, .sdiv = 0x3, .nsdiv = 1 }, /* 71.000 MHz */ - { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x3, .nsdiv = 1 }, /* 74.176 MHz */ - { .mdiv = 0x0f, .pe = 0x3426, .sdiv = 0x1, .nsdiv = 0 }, /* 74.250 MHz */ - { .mdiv = 0x0e, .pe = 0x7777, .sdiv = 0x1, .nsdiv = 0 }, /* 75.000 MHz */ - { .mdiv = 0x01, .pe = 0x4053, .sdiv = 0x3, .nsdiv = 1 }, /* 78.800 MHz */ - { .mdiv = 0x09, .pe = 0x15b5, .sdiv = 0x1, .nsdiv = 0 }, /* 85.500 MHz */ - { .mdiv = 0x1b, .pe = 0x3f19, .sdiv = 0x2, .nsdiv = 1 }, /* 88.750 MHz */ - { .mdiv = 0x10, .pe = 0x71c7, .sdiv = 0x2, .nsdiv = 1 }, /* 108.000 MHz */ - { .mdiv = 0x00, .pe = 0x47af, .sdiv = 0x1, .nsdiv = 0 }, /* 108.108 MHz */ - { .mdiv = 0x0c, .pe = 0x3118, .sdiv = 0x2, .nsdiv = 1 }, /* 118.963 MHz */ - { .mdiv = 0x0c, .pe = 0x2f54, .sdiv = 0x2, .nsdiv = 1 }, /* 119.000 MHz */ - { .mdiv = 0x07, .pe = 0xe39, .sdiv = 0x2, .nsdiv = 1 }, /* 135.000 MHz */ - { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x2, .nsdiv = 1 }, /* 148.352 MHz */ - { .mdiv = 0x0f, .pe = 0x3426, .sdiv = 0x0, .nsdiv = 0 }, /* 148.500 MHz */ - { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x1, .nsdiv = 1 }, /* 296.704 MHz */ - { .mdiv = 0x03, .pe = 0x471c, .sdiv = 0x1, .nsdiv = 1 }, /* 297.000 MHz */ - { .mdiv = 0x00, .pe = 0x295f, .sdiv = 0x1, .nsdiv = 1 }, /* 326.700 MHz */ - { .mdiv = 0x1f, .pe = 0x3633, .sdiv = 0x0, .nsdiv = 1 }, /* 333.000 MHz */ - { .mdiv = 0x1c, .pe = 0x0, .sdiv = 0x0, .nsdiv = 1 }, /* 352.000 Mhz */ -}; - struct clkgen_quadfs_data { bool reset_present; bool bwfilter_present; @@ -138,174 +65,18 @@ struct clkgen_quadfs_data { struct clkgen_field nsdiv[QUADFS_MAX_CHAN]; const struct clk_ops *pll_ops; - const struct stm_fs *rtbl; - u8 rtbl_cnt; + int (*get_params)(unsigned long, unsigned long, struct stm_fs *); int (*get_rate)(unsigned long , const struct stm_fs *, unsigned long *); }; -static const struct clk_ops st_quadfs_pll_c65_ops; static const struct clk_ops st_quadfs_pll_c32_ops; -static const struct clk_ops st_quadfs_fs216c65_ops; -static const struct clk_ops st_quadfs_fs432c65_ops; static const struct clk_ops st_quadfs_fs660c32_ops; -static int clk_fs216c65_get_rate(unsigned long, const struct stm_fs *, - unsigned long *); -static int clk_fs432c65_get_rate(unsigned long, const struct stm_fs *, - unsigned long *); +static int clk_fs660c32_dig_get_params(unsigned long input, + unsigned long output, struct stm_fs *fs); static int clk_fs660c32_dig_get_rate(unsigned long, const struct stm_fs *, unsigned long *); -/* - * Values for all of the standalone instances of this clock - * generator found in STiH415 and STiH416 SYSCFG register banks. Note - * that the individual channel standby control bits (nsb) are in the - * first register along with the PLL control bits. - */ -static const struct clkgen_quadfs_data st_fs216c65_416 = { - /* 416 specific */ - .npda = CLKGEN_FIELD(0x0, 0x1, 14), - .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), - CLKGEN_FIELD(0x0, 0x1, 11), - CLKGEN_FIELD(0x0, 0x1, 12), - CLKGEN_FIELD(0x0, 0x1, 13) }, - .nsdiv_present = true, - .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), - CLKGEN_FIELD(0x0, 0x1, 19), - CLKGEN_FIELD(0x0, 0x1, 20), - CLKGEN_FIELD(0x0, 0x1, 21) }, - .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), - CLKGEN_FIELD(0x14, 0x1f, 0), - CLKGEN_FIELD(0x24, 0x1f, 0), - CLKGEN_FIELD(0x34, 0x1f, 0) }, - .en = { CLKGEN_FIELD(0x10, 0x1, 0), - CLKGEN_FIELD(0x20, 0x1, 0), - CLKGEN_FIELD(0x30, 0x1, 0), - CLKGEN_FIELD(0x40, 0x1, 0) }, - .ndiv = CLKGEN_FIELD(0x0, 0x1, 15), - .bwfilter_present = true, - .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16), - .pe = { CLKGEN_FIELD(0x8, 0xffff, 0), - CLKGEN_FIELD(0x18, 0xffff, 0), - CLKGEN_FIELD(0x28, 0xffff, 0), - CLKGEN_FIELD(0x38, 0xffff, 0) }, - .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0), - CLKGEN_FIELD(0x1C, 0x7, 0), - CLKGEN_FIELD(0x2C, 0x7, 0), - CLKGEN_FIELD(0x3C, 0x7, 0) }, - .pll_ops = &st_quadfs_pll_c65_ops, - .rtbl = fs216c65_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs216c65_rtbl), - .get_rate = clk_fs216c65_get_rate, -}; - -static const struct clkgen_quadfs_data st_fs432c65_416 = { - .npda = CLKGEN_FIELD(0x0, 0x1, 14), - .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), - CLKGEN_FIELD(0x0, 0x1, 11), - CLKGEN_FIELD(0x0, 0x1, 12), - CLKGEN_FIELD(0x0, 0x1, 13) }, - .nsdiv_present = true, - .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), - CLKGEN_FIELD(0x0, 0x1, 19), - CLKGEN_FIELD(0x0, 0x1, 20), - CLKGEN_FIELD(0x0, 0x1, 21) }, - .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), - CLKGEN_FIELD(0x14, 0x1f, 0), - CLKGEN_FIELD(0x24, 0x1f, 0), - CLKGEN_FIELD(0x34, 0x1f, 0) }, - .en = { CLKGEN_FIELD(0x10, 0x1, 0), - CLKGEN_FIELD(0x20, 0x1, 0), - CLKGEN_FIELD(0x30, 0x1, 0), - CLKGEN_FIELD(0x40, 0x1, 0) }, - .ndiv = CLKGEN_FIELD(0x0, 0x1, 15), - .bwfilter_present = true, - .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16), - .pe = { CLKGEN_FIELD(0x8, 0xffff, 0), - CLKGEN_FIELD(0x18, 0xffff, 0), - CLKGEN_FIELD(0x28, 0xffff, 0), - CLKGEN_FIELD(0x38, 0xffff, 0) }, - .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0), - CLKGEN_FIELD(0x1C, 0x7, 0), - CLKGEN_FIELD(0x2C, 0x7, 0), - CLKGEN_FIELD(0x3C, 0x7, 0) }, - .pll_ops = &st_quadfs_pll_c65_ops, - .rtbl = fs432c65_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs432c65_rtbl), - .get_rate = clk_fs432c65_get_rate, -}; - -static const struct clkgen_quadfs_data st_fs660c32_E_416 = { - .npda = CLKGEN_FIELD(0x0, 0x1, 14), - .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), - CLKGEN_FIELD(0x0, 0x1, 11), - CLKGEN_FIELD(0x0, 0x1, 12), - CLKGEN_FIELD(0x0, 0x1, 13) }, - .nsdiv_present = true, - .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), - CLKGEN_FIELD(0x0, 0x1, 19), - CLKGEN_FIELD(0x0, 0x1, 20), - CLKGEN_FIELD(0x0, 0x1, 21) }, - .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), - CLKGEN_FIELD(0x14, 0x1f, 0), - CLKGEN_FIELD(0x24, 0x1f, 0), - CLKGEN_FIELD(0x34, 0x1f, 0) }, - .en = { CLKGEN_FIELD(0x10, 0x1, 0), - CLKGEN_FIELD(0x20, 0x1, 0), - CLKGEN_FIELD(0x30, 0x1, 0), - CLKGEN_FIELD(0x40, 0x1, 0) }, - .ndiv = CLKGEN_FIELD(0x0, 0x7, 15), - .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0), - CLKGEN_FIELD(0x18, 0x7fff, 0), - CLKGEN_FIELD(0x28, 0x7fff, 0), - CLKGEN_FIELD(0x38, 0x7fff, 0) }, - .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0), - CLKGEN_FIELD(0x1C, 0xf, 0), - CLKGEN_FIELD(0x2C, 0xf, 0), - CLKGEN_FIELD(0x3C, 0xf, 0) }, - .lockstatus_present = true, - .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0), - .pll_ops = &st_quadfs_pll_c32_ops, - .rtbl = fs660c32_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), - .get_rate = clk_fs660c32_dig_get_rate, -}; - -static const struct clkgen_quadfs_data st_fs660c32_F_416 = { - .npda = CLKGEN_FIELD(0x0, 0x1, 14), - .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), - CLKGEN_FIELD(0x0, 0x1, 11), - CLKGEN_FIELD(0x0, 0x1, 12), - CLKGEN_FIELD(0x0, 0x1, 13) }, - .nsdiv_present = true, - .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), - CLKGEN_FIELD(0x0, 0x1, 19), - CLKGEN_FIELD(0x0, 0x1, 20), - CLKGEN_FIELD(0x0, 0x1, 21) }, - .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), - CLKGEN_FIELD(0x14, 0x1f, 0), - CLKGEN_FIELD(0x24, 0x1f, 0), - CLKGEN_FIELD(0x34, 0x1f, 0) }, - .en = { CLKGEN_FIELD(0x10, 0x1, 0), - CLKGEN_FIELD(0x20, 0x1, 0), - CLKGEN_FIELD(0x30, 0x1, 0), - CLKGEN_FIELD(0x40, 0x1, 0) }, - .ndiv = CLKGEN_FIELD(0x0, 0x7, 15), - .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0), - CLKGEN_FIELD(0x18, 0x7fff, 0), - CLKGEN_FIELD(0x28, 0x7fff, 0), - CLKGEN_FIELD(0x38, 0x7fff, 0) }, - .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0), - CLKGEN_FIELD(0x1C, 0xf, 0), - CLKGEN_FIELD(0x2C, 0xf, 0), - CLKGEN_FIELD(0x3C, 0xf, 0) }, - .lockstatus_present = true, - .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0), - .pll_ops = &st_quadfs_pll_c32_ops, - .rtbl = fs660c32_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), - .get_rate = clk_fs660c32_dig_get_rate, -}; static const struct clkgen_quadfs_data st_fs660c32_C = { .nrst_present = true, @@ -345,8 +116,7 @@ static const struct clkgen_quadfs_data st_fs660c32_C = { .powerup_polarity = 1, .standby_polarity = 1, .pll_ops = &st_quadfs_pll_c32_ops, - .rtbl = fs660c32_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), + .get_params = clk_fs660c32_dig_get_params, .get_rate = clk_fs660c32_dig_get_rate, }; @@ -388,8 +158,7 @@ static const struct clkgen_quadfs_data st_fs660c32_D = { .powerup_polarity = 1, .standby_polarity = 1, .pll_ops = &st_quadfs_pll_c32_ops, - .rtbl = fs660c32_rtbl, - .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), + .get_params = clk_fs660c32_dig_get_params, .get_rate = clk_fs660c32_dig_get_rate,}; /** @@ -605,12 +374,6 @@ static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static const struct clk_ops st_quadfs_pll_c65_ops = { - .enable = quadfs_pll_enable, - .disable = quadfs_pll_disable, - .is_enabled = quadfs_pll_is_enabled, -}; - static const struct clk_ops st_quadfs_pll_c32_ops = { .enable = quadfs_pll_enable, .disable = quadfs_pll_disable, @@ -797,48 +560,6 @@ static int quadfs_fsynth_is_enabled(struct clk_hw *hw) return fs->data->standby_polarity ? !nsb : !!nsb; } -#define P15 (uint64_t)(1 << 15) - -static int clk_fs216c65_get_rate(unsigned long input, const struct stm_fs *fs, - unsigned long *rate) -{ - uint64_t res; - unsigned long ns; - unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */ - unsigned long s; - long m; - - m = fs->mdiv - 32; - s = 1 << (fs->sdiv + 1); - ns = (fs->nsdiv ? 1 : 3); - - res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33)); - res = res - (s * ns * fs->pe); - *rate = div64_u64(P15 * nd * input * 32, res); - - return 0; -} - -static int clk_fs432c65_get_rate(unsigned long input, const struct stm_fs *fs, - unsigned long *rate) -{ - uint64_t res; - unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */ - long m; - unsigned long sd; - unsigned long ns; - - m = fs->mdiv - 32; - sd = 1 << (fs->sdiv + 1); - ns = (fs->nsdiv ? 1 : 3); - - res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33)); - res = res - (sd * ns * fs->pe); - *rate = div64_u64(P15 * nd * input * 32, res); - - return 0; -} - #define P20 (uint64_t)(1 << 20) static int clk_fs660c32_dig_get_rate(unsigned long input, @@ -864,6 +585,107 @@ static int clk_fs660c32_dig_get_rate(unsigned long input, return 0; } + +static int clk_fs660c32_get_pe(int m, int si, unsigned long *deviation, + signed long input, unsigned long output, uint64_t *p, + struct stm_fs *fs) +{ + unsigned long new_freq, new_deviation; + struct stm_fs fs_tmp; + uint64_t val; + + val = (uint64_t)output << si; + + *p = (uint64_t)input * P20 - (32LL + (uint64_t)m) * val * (P20 / 32LL); + + *p = div64_u64(*p, val); + + if (*p > 32767LL) + return 1; + + fs_tmp.mdiv = (unsigned long) m; + fs_tmp.pe = (unsigned long)*p; + fs_tmp.sdiv = si; + fs_tmp.nsdiv = 1; + + clk_fs660c32_dig_get_rate(input, &fs_tmp, &new_freq); + + new_deviation = abs(output - new_freq); + + if (new_deviation < *deviation) { + fs->mdiv = m; + fs->pe = (unsigned long)*p; + fs->sdiv = si; + fs->nsdiv = 1; + *deviation = new_deviation; + } + return 0; +} + +static int clk_fs660c32_dig_get_params(unsigned long input, + unsigned long output, struct stm_fs *fs) +{ + int si; /* sdiv_reg (8 downto 0) */ + int m; /* md value */ + unsigned long new_freq, new_deviation; + /* initial condition to say: "infinite deviation" */ + unsigned long deviation = ~0; + uint64_t p, p1, p2; /* pe value */ + int r1, r2; + + struct stm_fs fs_tmp; + + for (si = 0; (si <= 8) && deviation; si++) { + + /* Boundary test to avoid useless iteration */ + r1 = clk_fs660c32_get_pe(0, si, &deviation, + input, output, &p1, fs); + r2 = clk_fs660c32_get_pe(31, si, &deviation, + input, output, &p2, fs); + + /* No solution */ + if (r1 && r2 && (p1 > p2)) + continue; + + /* Try to find best deviation */ + for (m = 1; (m < 31) && deviation; m++) + clk_fs660c32_get_pe(m, si, &deviation, + input, output, &p, fs); + + } + + if (deviation == ~0) /* No solution found */ + return -1; + + /* pe fine tuning if deviation not 0: +/- 2 around computed pe value */ + if (deviation) { + fs_tmp.mdiv = fs->mdiv; + fs_tmp.sdiv = fs->sdiv; + fs_tmp.nsdiv = fs->nsdiv; + + if (fs->pe > 2) + p2 = fs->pe - 2; + else + p2 = 0; + + for (; p2 < 32768ll && (p2 <= (fs->pe + 2)); p2++) { + fs_tmp.pe = (unsigned long)p2; + + clk_fs660c32_dig_get_rate(input, &fs_tmp, &new_freq); + + new_deviation = abs(output - new_freq); + + /* Check if this is a better solution */ + if (new_deviation < deviation) { + fs->pe = (unsigned long)p2; + deviation = new_deviation; + + } + } + } + return 0; +} + static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs, struct stm_fs *params) { @@ -899,38 +721,14 @@ static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate, struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); int (*clk_fs_get_rate)(unsigned long , const struct stm_fs *, unsigned long *); - struct stm_fs prev_params; - unsigned long prev_rate, rate = 0; - unsigned long diff_rate, prev_diff_rate = ~0; - int index; + int (*clk_fs_get_params)(unsigned long, unsigned long, struct stm_fs *); + unsigned long rate = 0; clk_fs_get_rate = fs->data->get_rate; + clk_fs_get_params = fs->data->get_params; - for (index = 0; index < fs->data->rtbl_cnt; index++) { - prev_rate = rate; - - *params = fs->data->rtbl[index]; - prev_params = *params; - - clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate); - - diff_rate = abs(drate - rate); - - if (diff_rate > prev_diff_rate) { - rate = prev_rate; - *params = prev_params; - break; - } - - prev_diff_rate = diff_rate; - - if (drate == rate) - return rate; - } - - - if (index == fs->data->rtbl_cnt) - *params = prev_params; + if (!clk_fs_get_params(prate, drate, params)) + clk_fs_get_rate(prate, params, &rate); return rate; } @@ -1063,34 +861,6 @@ static struct clk * __init st_clk_register_quadfs_fsynth( return clk; } -static const struct of_device_id quadfs_of_match[] = { - { - .compatible = "st,stih416-quadfs216", - .data = &st_fs216c65_416 - }, - { - .compatible = "st,stih416-quadfs432", - .data = &st_fs432c65_416 - }, - { - .compatible = "st,stih416-quadfs660-E", - .data = &st_fs660c32_E_416 - }, - { - .compatible = "st,stih416-quadfs660-F", - .data = &st_fs660c32_F_416 - }, - { - .compatible = "st,stih407-quadfs660-C", - .data = &st_fs660c32_C - }, - { - .compatible = "st,stih407-quadfs660-D", - .data = &st_fs660c32_D - }, - {} -}; - static void __init st_of_create_quadfs_fsynths( struct device_node *np, const char *pll_name, struct clkgen_quadfs_data *quadfs, void __iomem *reg, @@ -1150,18 +920,14 @@ static void __init st_of_create_quadfs_fsynths( of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); } -static void __init st_of_quadfs_setup(struct device_node *np) +static void __init st_of_quadfs_setup(struct device_node *np, + struct clkgen_quadfs_data *data) { - const struct of_device_id *match; struct clk *clk; const char *pll_name, *clk_parent_name; void __iomem *reg; spinlock_t *lock; - match = of_match_node(quadfs_of_match, np); - if (WARN_ON(!match)) - return; - reg = of_iomap(np, 0); if (!reg) return; @@ -1180,8 +946,8 @@ static void __init st_of_quadfs_setup(struct device_node *np) spin_lock_init(lock); - clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, - (struct clkgen_quadfs_data *) match->data, reg, lock); + clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, data, + reg, lock); if (IS_ERR(clk)) goto err_exit; else @@ -1190,11 +956,20 @@ static void __init st_of_quadfs_setup(struct device_node *np) __clk_get_name(clk_get_parent(clk)), (unsigned int)clk_get_rate(clk)); - st_of_create_quadfs_fsynths(np, pll_name, - (struct clkgen_quadfs_data *)match->data, - reg, lock); + st_of_create_quadfs_fsynths(np, pll_name, data, reg, lock); err_exit: kfree(pll_name); /* No longer need local copy of the PLL name */ } -CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup); + +static void __init st_of_quadfs660C_setup(struct device_node *np) +{ + st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_C); +} +CLK_OF_DECLARE(quadfs660C, "st,quadfs-pll", st_of_quadfs660C_setup); + +static void __init st_of_quadfs660D_setup(struct device_node *np) +{ + st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_D); +} +CLK_OF_DECLARE(quadfs660D, "st,quadfs", st_of_quadfs660D_setup); diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index b1e10ff..c514d39 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -19,9 +19,6 @@ #include <linux/clk-provider.h> #include "clkgen.h" -static DEFINE_SPINLOCK(clkgena_divmux_lock); -static DEFINE_SPINLOCK(clkgenf_lock); - static const char ** __init clkgen_mux_get_parents(struct device_node *np, int *num_parents) { @@ -40,498 +37,6 @@ static const char ** __init clkgen_mux_get_parents(struct device_node *np, return parents; } -/** - * DOC: Clock mux with a programmable divider on each of its three inputs. - * The mux has an input setting which effectively gates its output. - * - * Traits of this clock: - * prepare - clk_(un)prepare only ensures parent is (un)prepared - * enable - clk_enable and clk_disable are functional & control gating - * rate - set rate is supported - * parent - set/get parent - */ - -#define NUM_INPUTS 3 - -struct clkgena_divmux { - struct clk_hw hw; - /* Subclassed mux and divider structures */ - struct clk_mux mux; - struct clk_divider div[NUM_INPUTS]; - /* Enable/running feedback register bits for each input */ - void __iomem *feedback_reg[NUM_INPUTS]; - int feedback_bit_idx; - - u8 muxsel; -}; - -#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) - -struct clkgena_divmux_data { - int num_outputs; - int mux_offset; - int mux_offset2; - int mux_start_bit; - int div_offsets[NUM_INPUTS]; - int fb_offsets[NUM_INPUTS]; - int fb_start_bit_idx; -}; - -#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 - -static int clkgena_divmux_is_running(struct clkgena_divmux *mux) -{ - u32 regval = readl(mux->feedback_reg[mux->muxsel]); - u32 running = regval & BIT(mux->feedback_bit_idx); - return !!running; -} - -static int clkgena_divmux_enable(struct clk_hw *hw) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *mux_hw = &genamux->mux.hw; - unsigned long timeout; - int ret = 0; - - __clk_hw_set_clk(mux_hw, hw); - - ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); - if (ret) - return ret; - - timeout = jiffies + msecs_to_jiffies(10); - - while (!clkgena_divmux_is_running(genamux)) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} - -static void clkgena_divmux_disable(struct clk_hw *hw) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *mux_hw = &genamux->mux.hw; - - __clk_hw_set_clk(mux_hw, hw); - - clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); -} - -static int clkgena_divmux_is_enabled(struct clk_hw *hw) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *mux_hw = &genamux->mux.hw; - - __clk_hw_set_clk(mux_hw, hw); - - return (s8)clk_mux_ops.get_parent(mux_hw) > 0; -} - -static u8 clkgena_divmux_get_parent(struct clk_hw *hw) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *mux_hw = &genamux->mux.hw; - - __clk_hw_set_clk(mux_hw, hw); - - genamux->muxsel = clk_mux_ops.get_parent(mux_hw); - if ((s8)genamux->muxsel < 0) { - pr_debug("%s: %s: Invalid parent, setting to default.\n", - __func__, clk_hw_get_name(hw)); - genamux->muxsel = 0; - } - - return genamux->muxsel; -} - -static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - - if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) - return -EINVAL; - - genamux->muxsel = index; - - /* - * If the mux is already enabled, call enable directly to set the - * new mux position and wait for it to start running again. Otherwise - * do nothing. - */ - if (clkgena_divmux_is_enabled(hw)) - clkgena_divmux_enable(hw); - - return 0; -} - -static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; - - __clk_hw_set_clk(div_hw, hw); - - return clk_divider_ops.recalc_rate(div_hw, parent_rate); -} - -static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; - - __clk_hw_set_clk(div_hw, hw); - - return clk_divider_ops.set_rate(div_hw, rate, parent_rate); -} - -static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - struct clkgena_divmux *genamux = to_clkgena_divmux(hw); - struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; - - __clk_hw_set_clk(div_hw, hw); - - return clk_divider_ops.round_rate(div_hw, rate, prate); -} - -static const struct clk_ops clkgena_divmux_ops = { - .enable = clkgena_divmux_enable, - .disable = clkgena_divmux_disable, - .is_enabled = clkgena_divmux_is_enabled, - .get_parent = clkgena_divmux_get_parent, - .set_parent = clkgena_divmux_set_parent, - .round_rate = clkgena_divmux_round_rate, - .recalc_rate = clkgena_divmux_recalc_rate, - .set_rate = clkgena_divmux_set_rate, -}; - -/** - * clk_register_genamux - register a genamux clock with the clock framework - */ -static struct clk * __init clk_register_genamux(const char *name, - const char **parent_names, u8 num_parents, - void __iomem *reg, - const struct clkgena_divmux_data *muxdata, - u32 idx) -{ - /* - * Fixed constants across all ClockgenA variants - */ - const int mux_width = 2; - const int divider_width = 5; - struct clkgena_divmux *genamux; - struct clk *clk; - struct clk_init_data init; - int i; - - genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); - if (!genamux) - return ERR_PTR(-ENOMEM); - - init.name = name; - init.ops = &clkgena_divmux_ops; - init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE; - init.parent_names = parent_names; - init.num_parents = num_parents; - - genamux->mux.lock = &clkgena_divmux_lock; - genamux->mux.mask = BIT(mux_width) - 1; - genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); - if (genamux->mux.shift > 31) { - /* - * We have spilled into the second mux register so - * adjust the register address and the bit shift accordingly - */ - genamux->mux.reg = reg + muxdata->mux_offset2; - genamux->mux.shift -= 32; - } else { - genamux->mux.reg = reg + muxdata->mux_offset; - } - - for (i = 0; i < NUM_INPUTS; i++) { - /* - * Divider config for each input - */ - void __iomem *divbase = reg + muxdata->div_offsets[i]; - genamux->div[i].width = divider_width; - genamux->div[i].reg = divbase + (idx * sizeof(u32)); - - /* - * Mux enabled/running feedback register for each input. - */ - genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; - } - - genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; - genamux->hw.init = &init; - - clk = clk_register(NULL, &genamux->hw); - if (IS_ERR(clk)) { - kfree(genamux); - goto err; - } - - pr_debug("%s: parent %s rate %lu\n", - __clk_get_name(clk), - __clk_get_name(clk_get_parent(clk)), - clk_get_rate(clk)); -err: - return clk; -} - -static struct clkgena_divmux_data st_divmux_c65hs = { - .num_outputs = 4, - .mux_offset = 0x14, - .mux_start_bit = 0, - .div_offsets = { 0x800, 0x900, 0xb00 }, - .fb_offsets = { 0x18, 0x1c, 0x20 }, - .fb_start_bit_idx = 0, -}; - -static struct clkgena_divmux_data st_divmux_c65ls = { - .num_outputs = 14, - .mux_offset = 0x14, - .mux_offset2 = 0x24, - .mux_start_bit = 8, - .div_offsets = { 0x810, 0xa10, 0xb10 }, - .fb_offsets = { 0x18, 0x1c, 0x20 }, - .fb_start_bit_idx = 4, -}; - -static struct clkgena_divmux_data st_divmux_c32odf0 = { - .num_outputs = 8, - .mux_offset = 0x1c, - .mux_start_bit = 0, - .div_offsets = { 0x800, 0x900, 0xa60 }, - .fb_offsets = { 0x2c, 0x24, 0x28 }, - .fb_start_bit_idx = 0, -}; - -static struct clkgena_divmux_data st_divmux_c32odf1 = { - .num_outputs = 8, - .mux_offset = 0x1c, - .mux_start_bit = 16, - .div_offsets = { 0x820, 0x980, 0xa80 }, - .fb_offsets = { 0x2c, 0x24, 0x28 }, - .fb_start_bit_idx = 8, -}; - -static struct clkgena_divmux_data st_divmux_c32odf2 = { - .num_outputs = 8, - .mux_offset = 0x20, - .mux_start_bit = 0, - .div_offsets = { 0x840, 0xa20, 0xb10 }, - .fb_offsets = { 0x2c, 0x24, 0x28 }, - .fb_start_bit_idx = 16, -}; - -static struct clkgena_divmux_data st_divmux_c32odf3 = { - .num_outputs = 8, - .mux_offset = 0x20, - .mux_start_bit = 16, - .div_offsets = { 0x860, 0xa40, 0xb30 }, - .fb_offsets = { 0x2c, 0x24, 0x28 }, - .fb_start_bit_idx = 24, -}; - -static const struct of_device_id clkgena_divmux_of_match[] = { - { - .compatible = "st,clkgena-divmux-c65-hs", - .data = &st_divmux_c65hs, - }, - { - .compatible = "st,clkgena-divmux-c65-ls", - .data = &st_divmux_c65ls, - }, - { - .compatible = "st,clkgena-divmux-c32-odf0", - .data = &st_divmux_c32odf0, - }, - { - .compatible = "st,clkgena-divmux-c32-odf1", - .data = &st_divmux_c32odf1, - }, - { - .compatible = "st,clkgena-divmux-c32-odf2", - .data = &st_divmux_c32odf2, - }, - { - .compatible = "st,clkgena-divmux-c32-odf3", - .data = &st_divmux_c32odf3, - }, - {} -}; - -static void __iomem * __init clkgen_get_register_base(struct device_node *np) -{ - struct device_node *pnode; - void __iomem *reg; - - pnode = of_get_parent(np); - if (!pnode) - return NULL; - - reg = of_iomap(pnode, 0); - - of_node_put(pnode); - return reg; -} - -static void __init st_of_clkgena_divmux_setup(struct device_node *np) -{ - const struct of_device_id *match; - const struct clkgena_divmux_data *data; - struct clk_onecell_data *clk_data; - void __iomem *reg; - const char **parents; - int num_parents = 0, i; - - match = of_match_node(clkgena_divmux_of_match, np); - if (WARN_ON(!match)) - return; - - data = match->data; - - reg = clkgen_get_register_base(np); - if (!reg) - return; - - parents = clkgen_mux_get_parents(np, &num_parents); - if (IS_ERR(parents)) - goto err_parents; - - clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); - if (!clk_data) - goto err_alloc; - - clk_data->clk_num = data->num_outputs; - clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), - GFP_KERNEL); - - if (!clk_data->clks) - goto err_alloc_clks; - - for (i = 0; i < clk_data->clk_num; i++) { - struct clk *clk; - const char *clk_name; - - if (of_property_read_string_index(np, "clock-output-names", - i, &clk_name)) - break; - - /* - * If we read an empty clock name then the output is unused - */ - if (*clk_name == '\0') - continue; - - clk = clk_register_genamux(clk_name, parents, num_parents, - reg, data, i); - - if (IS_ERR(clk)) - goto err; - - clk_data->clks[i] = clk; - } - - kfree(parents); - - of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); - return; -err: - kfree(clk_data->clks); -err_alloc_clks: - kfree(clk_data); -err_alloc: - kfree(parents); -err_parents: - iounmap(reg); -} -CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); - -struct clkgena_prediv_data { - u32 offset; - u8 shift; - struct clk_div_table *table; -}; - -static struct clk_div_table prediv_table16[] = { - { .val = 0, .div = 1 }, - { .val = 1, .div = 16 }, - { .div = 0 }, -}; - -static struct clkgena_prediv_data prediv_c65_data = { - .offset = 0x4c, - .shift = 31, - .table = prediv_table16, -}; - -static struct clkgena_prediv_data prediv_c32_data = { - .offset = 0x50, - .shift = 1, - .table = prediv_table16, -}; - -static const struct of_device_id clkgena_prediv_of_match[] = { - { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, - { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, - {} -}; - -static void __init st_of_clkgena_prediv_setup(struct device_node *np) -{ - const struct of_device_id *match; - void __iomem *reg; - const char *parent_name, *clk_name; - struct clk *clk; - const struct clkgena_prediv_data *data; - - match = of_match_node(clkgena_prediv_of_match, np); - if (!match) { - pr_err("%s: No matching data\n", __func__); - return; - } - - data = match->data; - - reg = clkgen_get_register_base(np); - if (!reg) - return; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - goto err; - - if (of_property_read_string_index(np, "clock-output-names", - 0, &clk_name)) - goto err; - - clk = clk_register_divider_table(NULL, clk_name, parent_name, - CLK_GET_RATE_NOCACHE, - reg + data->offset, data->shift, 1, - 0, data->table, NULL); - if (IS_ERR(clk)) - goto err; - - of_clk_add_provider(np, of_clk_src_simple_get, clk); - pr_debug("%s: parent %s rate %u\n", - __clk_get_name(clk), - __clk_get_name(clk_get_parent(clk)), - (unsigned int)clk_get_rate(clk)); - - return; -err: - iounmap(reg); -} -CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); - struct clkgen_mux_data { u32 offset; u8 shift; @@ -541,49 +46,6 @@ struct clkgen_mux_data { u8 mux_flags; }; -static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { - .offset = 0, - .shift = 0, - .width = 1, -}; - -static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { - .offset = 0, - .shift = 0, - .width = 1, -}; - -static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { - .offset = 0, - .shift = 0, - .width = 1, -}; - -static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { - .offset = 0, - .shift = 16, - .width = 1, - .lock = &clkgenf_lock, -}; - -static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { - .offset = 0, - .shift = 17, - .width = 1, - .lock = &clkgenf_lock, -}; - -static struct clkgen_mux_data stih415_a9_mux_data = { - .offset = 0, - .shift = 1, - .width = 2, - .lock = &clkgen_a9_lock, -}; -static struct clkgen_mux_data stih416_a9_mux_data = { - .offset = 0, - .shift = 0, - .width = 2, -}; static struct clkgen_mux_data stih407_a9_mux_data = { .offset = 0x1a4, .shift = 0, @@ -591,58 +53,13 @@ static struct clkgen_mux_data stih407_a9_mux_data = { .lock = &clkgen_a9_lock, }; -static const struct of_device_id mux_of_match[] = { - { - .compatible = "st,stih416-clkgenc-vcc-hd", - .data = &clkgen_mux_c_vcc_hd_416, - }, - { - .compatible = "st,stih416-clkgenf-vcc-fvdp", - .data = &clkgen_mux_f_vcc_fvdp_416, - }, - { - .compatible = "st,stih416-clkgenf-vcc-hva", - .data = &clkgen_mux_f_vcc_hva_416, - }, - { - .compatible = "st,stih416-clkgenf-vcc-hd", - .data = &clkgen_mux_f_vcc_hd_416, - }, - { - .compatible = "st,stih416-clkgenf-vcc-sd", - .data = &clkgen_mux_c_vcc_sd_416, - }, - { - .compatible = "st,stih415-clkgen-a9-mux", - .data = &stih415_a9_mux_data, - }, - { - .compatible = "st,stih416-clkgen-a9-mux", - .data = &stih416_a9_mux_data, - }, - { - .compatible = "st,stih407-clkgen-a9-mux", - .data = &stih407_a9_mux_data, - }, - {} -}; - -static void __init st_of_clkgen_mux_setup(struct device_node *np) +static void __init st_of_clkgen_mux_setup(struct device_node *np, + struct clkgen_mux_data *data) { - const struct of_device_id *match; struct clk *clk; void __iomem *reg; const char **parents; - int num_parents; - const struct clkgen_mux_data *data; - - match = of_match_node(mux_of_match, np); - if (!match) { - pr_err("%s: No matching data\n", __func__); - return; - } - - data = match->data; + int num_parents = 0; reg = of_iomap(np, 0); if (!reg) { @@ -679,161 +96,10 @@ err: err_parents: iounmap(reg); } -CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); - -#define VCC_MAX_CHANNELS 16 - -#define VCC_GATE_OFFSET 0x0 -#define VCC_MUX_OFFSET 0x4 -#define VCC_DIV_OFFSET 0x8 - -struct clkgen_vcc_data { - spinlock_t *lock; - unsigned long clk_flags; -}; - -static struct clkgen_vcc_data st_clkgenc_vcc_416 = { - .clk_flags = CLK_SET_RATE_PARENT, -}; - -static struct clkgen_vcc_data st_clkgenf_vcc_416 = { - .lock = &clkgenf_lock, -}; - -static const struct of_device_id vcc_of_match[] = { - { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, - { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, - {} -}; -static void __init st_of_clkgen_vcc_setup(struct device_node *np) +static void __init st_of_clkgen_a9_mux_setup(struct device_node *np) { - const struct of_device_id *match; - void __iomem *reg; - const char **parents; - int num_parents, i; - struct clk_onecell_data *clk_data; - const struct clkgen_vcc_data *data; - - match = of_match_node(vcc_of_match, np); - if (WARN_ON(!match)) - return; - data = match->data; - - reg = of_iomap(np, 0); - if (!reg) - return; - - parents = clkgen_mux_get_parents(np, &num_parents); - if (IS_ERR(parents)) - goto err_parents; - - clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); - if (!clk_data) - goto err_alloc; - - clk_data->clk_num = VCC_MAX_CHANNELS; - clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), - GFP_KERNEL); - - if (!clk_data->clks) - goto err_alloc_clks; - - for (i = 0; i < clk_data->clk_num; i++) { - struct clk *clk; - const char *clk_name; - struct clk_gate *gate; - struct clk_divider *div; - struct clk_mux *mux; - - if (of_property_read_string_index(np, "clock-output-names", - i, &clk_name)) - break; - - /* - * If we read an empty clock name then the output is unused - */ - if (*clk_name == '\0') - continue; - - gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) - goto err; - - div = kzalloc(sizeof(*div), GFP_KERNEL); - if (!div) { - kfree(gate); - goto err; - } - - mux = kzalloc(sizeof(*mux), GFP_KERNEL); - if (!mux) { - kfree(gate); - kfree(div); - goto err; - } - - gate->reg = reg + VCC_GATE_OFFSET; - gate->bit_idx = i; - gate->flags = CLK_GATE_SET_TO_DISABLE; - gate->lock = data->lock; - - div->reg = reg + VCC_DIV_OFFSET; - div->shift = 2 * i; - div->width = 2; - div->flags = CLK_DIVIDER_POWER_OF_TWO | - CLK_DIVIDER_ROUND_CLOSEST; - - mux->reg = reg + VCC_MUX_OFFSET; - mux->shift = 2 * i; - mux->mask = 0x3; - - clk = clk_register_composite(NULL, clk_name, parents, - num_parents, - &mux->hw, &clk_mux_ops, - &div->hw, &clk_divider_ops, - &gate->hw, &clk_gate_ops, - data->clk_flags | - CLK_GET_RATE_NOCACHE); - if (IS_ERR(clk)) { - kfree(gate); - kfree(div); - kfree(mux); - goto err; - } - - pr_debug("%s: parent %s rate %u\n", - __clk_get_name(clk), - __clk_get_name(clk_get_parent(clk)), - (unsigned int)clk_get_rate(clk)); - - clk_data->clks[i] = clk; - } - - kfree(parents); - - of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); - return; - -err: - for (i = 0; i < clk_data->clk_num; i++) { - struct clk_composite *composite; - - if (!clk_data->clks[i]) - continue; - - composite = to_clk_composite(__clk_get_hw(clk_data->clks[i])); - kfree(to_clk_gate(composite->gate_hw)); - kfree(to_clk_divider(composite->rate_hw)); - kfree(to_clk_mux(composite->mux_hw)); - } - - kfree(clk_data->clks); -err_alloc_clks: - kfree(clk_data); -err_alloc: - kfree(parents); -err_parents: - iounmap(reg); + st_of_clkgen_mux_setup(np, &stih407_a9_mux_data); } -CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); +CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux", + st_of_clkgen_a9_mux_setup); diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 0b5990e..25bda48 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -26,14 +26,6 @@ static DEFINE_SPINLOCK(clkgena_c32_odf_lock); DEFINE_SPINLOCK(clkgen_a9_lock); /* - * Common PLL configuration register bits for PLL800 and PLL1600 C65 - */ -#define C65_MDIV_PLL800_MASK (0xff) -#define C65_MDIV_PLL1600_MASK (0x7) -#define C65_NDIV_MASK (0xff) -#define C65_PDIV_MASK (0x7) - -/* * PLL configuration register bits for PLL3200 C32 */ #define C32_NDIV_MASK (0xff) @@ -70,144 +62,10 @@ struct clkgen_pll_data { const struct clk_ops *ops; }; -static const struct clk_ops st_pll1600c65_ops; -static const struct clk_ops st_pll800c65_ops; static const struct clk_ops stm_pll3200c32_ops; static const struct clk_ops stm_pll3200c32_a9_ops; -static const struct clk_ops st_pll1200c32_ops; static const struct clk_ops stm_pll4600c28_ops; -static const struct clkgen_pll_data st_pll1600c65_ax = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19), - .pdn_ctrl = CLKGEN_FIELD(0x10, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x0, 0x1, 31), - .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0), - .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8), - .ops = &st_pll1600c65_ops -}; - -static const struct clkgen_pll_data st_pll800c65_ax = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19), - .pdn_ctrl = CLKGEN_FIELD(0xC, 0x1, 1), - .locked_status = CLKGEN_FIELD(0x0, 0x1, 31), - .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0), - .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8), - .pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16), - .ops = &st_pll800c65_ops -}; - -static const struct clkgen_pll_data st_pll3200c32_a1x_0 = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 31), - .pdn_ctrl = CLKGEN_FIELD(0x18, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x4, 0x1, 31), - .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0), - .idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0), - .num_odfs = 4, - .odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4), - CLKGEN_FIELD(0x54, C32_ODF_MASK, 10), - CLKGEN_FIELD(0x54, C32_ODF_MASK, 16), - CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) }, - .odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0), - CLKGEN_FIELD(0x54, 0x1, 1), - CLKGEN_FIELD(0x54, 0x1, 2), - CLKGEN_FIELD(0x54, 0x1, 3) }, - .ops = &stm_pll3200c32_ops, -}; - -static const struct clkgen_pll_data st_pll3200c32_a1x_1 = { - .pdn_status = CLKGEN_FIELD(0xC, 0x1, 31), - .pdn_ctrl = CLKGEN_FIELD(0x18, 0x1, 1), - .locked_status = CLKGEN_FIELD(0x10, 0x1, 31), - .ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0), - .idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0), - .num_odfs = 4, - .odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4), - CLKGEN_FIELD(0x58, C32_ODF_MASK, 10), - CLKGEN_FIELD(0x58, C32_ODF_MASK, 16), - CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) }, - .odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0), - CLKGEN_FIELD(0x58, 0x1, 1), - CLKGEN_FIELD(0x58, 0x1, 2), - CLKGEN_FIELD(0x58, 0x1, 3) }, - .ops = &stm_pll3200c32_ops, -}; - -/* 415 specific */ -static const struct clkgen_pll_data st_pll3200c32_a9_415 = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), - .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0), - .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22), - .num_odfs = 1, - .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) }, - .odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) }, - .ops = &stm_pll3200c32_ops, -}; - -static const struct clkgen_pll_data st_pll3200c32_ddr_415 = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), - .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x100, 0x1, 0), - .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), - .num_odfs = 2, - .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8), - CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) }, - .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28), - CLKGEN_FIELD(0x4, 0x1, 29) }, - .ops = &stm_pll3200c32_ops, -}; - -static const struct clkgen_pll_data st_pll1200c32_gpu_415 = { - .pdn_status = CLKGEN_FIELD(0x4, 0x1, 0), - .pdn_ctrl = CLKGEN_FIELD(0x4, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x168, 0x1, 0), - .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0), - .num_odfs = 0, - .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) }, - .ops = &st_pll1200c32_ops, -}; - -/* 416 specific */ -static const struct clkgen_pll_data st_pll3200c32_a9_416 = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), - .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0), - .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), - .num_odfs = 1, - .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) }, - .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) }, - .ops = &stm_pll3200c32_ops, -}; - -static const struct clkgen_pll_data st_pll3200c32_ddr_416 = { - .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), - .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0), - .locked_status = CLKGEN_FIELD(0x10C, 0x1, 0), - .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), - .num_odfs = 2, - .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8), - CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) }, - .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28), - CLKGEN_FIELD(0x4, 0x1, 29) }, - .ops = &stm_pll3200c32_ops, -}; - -static const struct clkgen_pll_data st_pll1200c32_gpu_416 = { - .pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3), - .pdn_ctrl = CLKGEN_FIELD(0x8E4, 0x1, 3), - .locked_status = CLKGEN_FIELD(0x90C, 0x1, 0), - .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3), - .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0), - .num_odfs = 0, - .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) }, - .ops = &st_pll1200c32_ops, -}; - static const struct clkgen_pll_data st_pll3200c32_407_a0 = { /* 407 A0 */ .pdn_status = CLKGEN_FIELD(0x2a0, 0x1, 8), @@ -410,57 +268,6 @@ static void clkgen_pll_disable(struct clk_hw *hw) spin_unlock_irqrestore(pll->lock, flags); } -static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clkgen_pll *pll = to_clkgen_pll(hw); - unsigned long mdiv, ndiv, pdiv; - unsigned long rate; - uint64_t res; - - if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) - return 0; - - pdiv = CLKGEN_READ(pll, pdiv); - mdiv = CLKGEN_READ(pll, mdiv); - ndiv = CLKGEN_READ(pll, ndiv); - - if (!mdiv) - mdiv++; /* mdiv=0 or 1 => MDIV=1 */ - - res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv; - rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv)); - - pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate); - - return rate; - -} - -static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clkgen_pll *pll = to_clkgen_pll(hw); - unsigned long mdiv, ndiv; - unsigned long rate; - - if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) - return 0; - - mdiv = CLKGEN_READ(pll, mdiv); - ndiv = CLKGEN_READ(pll, ndiv); - - if (!mdiv) - mdiv = 1; - - /* Note: input is divided by 1000 to avoid overflow */ - rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000; - - pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate); - - return rate; -} - static int clk_pll3200c32_get_params(unsigned long input, unsigned long output, struct stm_pll *pll) { @@ -608,33 +415,6 @@ static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, return 0; } -static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clkgen_pll *pll = to_clkgen_pll(hw); - unsigned long odf, ldf, idf; - unsigned long rate; - - if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) - return 0; - - odf = CLKGEN_READ(pll, odf[0]); - ldf = CLKGEN_READ(pll, ldf); - idf = CLKGEN_READ(pll, idf); - - if (!idf) /* idf==0 means 1 */ - idf = 1; - if (!odf) /* odf==0 means 1 */ - odf = 1; - - /* Note: input is divided by 1000 to avoid overflow */ - rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000; - - pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate); - - return rate; -} - /* PLL output structure * FVCO >> /2 >> FVCOBY2 (no output) * |> Divider (ODF) >> PHI @@ -792,20 +572,6 @@ static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, return 0; } -static const struct clk_ops st_pll1600c65_ops = { - .enable = clkgen_pll_enable, - .disable = clkgen_pll_disable, - .is_enabled = clkgen_pll_is_enabled, - .recalc_rate = recalc_stm_pll1600c65, -}; - -static const struct clk_ops st_pll800c65_ops = { - .enable = clkgen_pll_enable, - .disable = clkgen_pll_disable, - .is_enabled = clkgen_pll_is_enabled, - .recalc_rate = recalc_stm_pll800c65, -}; - static const struct clk_ops stm_pll3200c32_ops = { .enable = clkgen_pll_enable, .disable = clkgen_pll_disable, @@ -822,13 +588,6 @@ static const struct clk_ops stm_pll3200c32_a9_ops = { .set_rate = set_rate_stm_pll3200c32, }; -static const struct clk_ops st_pll1200c32_ops = { - .enable = clkgen_pll_enable, - .disable = clkgen_pll_disable, - .is_enabled = clkgen_pll_is_enabled, - .recalc_rate = recalc_stm_pll1200c32, -}; - static const struct clk_ops stm_pll4600c28_ops = { .enable = clkgen_pll_enable, .disable = clkgen_pll_disable, @@ -877,22 +636,6 @@ static struct clk * __init clkgen_pll_register(const char *parent_name, return clk; } -static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name, - const char *clk_name) -{ - struct clk *clk; - - clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2); - if (IS_ERR(clk)) - return clk; - - pr_debug("%s: parent %s rate %lu\n", - __clk_get_name(clk), - __clk_get_name(clk_get_parent(clk)), - clk_get_rate(clk)); - return clk; -} - static void __iomem * __init clkgen_get_register_base( struct device_node *np) { @@ -909,89 +652,6 @@ static void __iomem * __init clkgen_get_register_base( return reg; } -#define CLKGENAx_PLL0_OFFSET 0x0 -#define CLKGENAx_PLL1_OFFSET 0x4 - -static void __init clkgena_c65_pll_setup(struct device_node *np) -{ - const int num_pll_outputs = 3; - struct clk_onecell_data *clk_data; - const char *parent_name; - void __iomem *reg; - const char *clk_name; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - return; - - reg = clkgen_get_register_base(np); - if (!reg) - return; - - clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); - if (!clk_data) - return; - - clk_data->clk_num = num_pll_outputs; - clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), - GFP_KERNEL); - - if (!clk_data->clks) - goto err; - - if (of_property_read_string_index(np, "clock-output-names", - 0, &clk_name)) - goto err; - - /* - * PLL0 HS (high speed) output - */ - clk_data->clks[0] = clkgen_pll_register(parent_name, - (struct clkgen_pll_data *) &st_pll1600c65_ax, - reg + CLKGENAx_PLL0_OFFSET, 0, clk_name, NULL); - - if (IS_ERR(clk_data->clks[0])) - goto err; - - if (of_property_read_string_index(np, "clock-output-names", - 1, &clk_name)) - goto err; - - /* - * PLL0 LS (low speed) output, which is a fixed divide by 2 of the - * high speed output. - */ - clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name - (clk_data->clks[0]), - clk_name); - - if (IS_ERR(clk_data->clks[1])) - goto err; - - if (of_property_read_string_index(np, "clock-output-names", - 2, &clk_name)) - goto err; - - /* - * PLL1 output - */ - clk_data->clks[2] = clkgen_pll_register(parent_name, - (struct clkgen_pll_data *) &st_pll800c65_ax, - reg + CLKGENAx_PLL1_OFFSET, 0, clk_name, NULL); - - if (IS_ERR(clk_data->clks[2])) - goto err; - - of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); - return; - -err: - kfree(clk_data->clks); - kfree(clk_data); -} -CLK_OF_DECLARE(clkgena_c65_plls, - "st,clkgena-plls-c65", clkgena_c65_pll_setup); - static struct clk * __init clkgen_odf_register(const char *parent_name, void __iomem *reg, struct clkgen_pll_data *pll_data, @@ -1042,72 +702,17 @@ static struct clk * __init clkgen_odf_register(const char *parent_name, return clk; } -static const struct of_device_id c32_pll_of_match[] = { - { - .compatible = "st,plls-c32-a1x-0", - .data = &st_pll3200c32_a1x_0, - }, - { - .compatible = "st,plls-c32-a1x-1", - .data = &st_pll3200c32_a1x_1, - }, - { - .compatible = "st,stih415-plls-c32-a9", - .data = &st_pll3200c32_a9_415, - }, - { - .compatible = "st,stih415-plls-c32-ddr", - .data = &st_pll3200c32_ddr_415, - }, - { - .compatible = "st,stih416-plls-c32-a9", - .data = &st_pll3200c32_a9_416, - }, - { - .compatible = "st,stih416-plls-c32-ddr", - .data = &st_pll3200c32_ddr_416, - }, - { - .compatible = "st,stih407-plls-c32-a0", - .data = &st_pll3200c32_407_a0, - }, - { - .compatible = "st,plls-c32-cx_0", - .data = &st_pll3200c32_cx_0, - }, - { - .compatible = "st,plls-c32-cx_1", - .data = &st_pll3200c32_cx_1, - }, - { - .compatible = "st,stih407-plls-c32-a9", - .data = &st_pll3200c32_407_a9, - }, - { - .compatible = "st,stih418-plls-c28-a9", - .data = &st_pll4600c28_418_a9, - }, - {} -}; -static void __init clkgen_c32_pll_setup(struct device_node *np) +static void __init clkgen_c32_pll_setup(struct device_node *np, + struct clkgen_pll_data *data) { - const struct of_device_id *match; struct clk *clk; const char *parent_name, *pll_name; void __iomem *pll_base; int num_odfs, odf; struct clk_onecell_data *clk_data; - struct clkgen_pll_data *data; unsigned long pll_flags = 0; - match = of_match_node(c32_pll_of_match, np); - if (!match) { - pr_err("%s: No matching data\n", __func__); - return; - } - - data = (struct clkgen_pll_data *) match->data; parent_name = of_clk_get_parent_name(np, 0); if (!parent_name) @@ -1166,59 +771,30 @@ err: kfree(clk_data->clks); kfree(clk_data); } -CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup); - -static const struct of_device_id c32_gpu_pll_of_match[] = { - { - .compatible = "st,stih415-gpu-pll-c32", - .data = &st_pll1200c32_gpu_415, - }, - { - .compatible = "st,stih416-gpu-pll-c32", - .data = &st_pll1200c32_gpu_416, - }, - {} -}; - -static void __init clkgengpu_c32_pll_setup(struct device_node *np) +static void __init clkgen_c32_pll0_setup(struct device_node *np) { - const struct of_device_id *match; - struct clk *clk; - const char *parent_name; - void __iomem *reg; - const char *clk_name; - struct clkgen_pll_data *data; - - match = of_match_node(c32_gpu_pll_of_match, np); - if (!match) { - pr_err("%s: No matching data\n", __func__); - return; - } - - data = (struct clkgen_pll_data *)match->data; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - return; - - reg = clkgen_get_register_base(np); - if (!reg) - return; - - if (of_property_read_string_index(np, "clock-output-names", - 0, &clk_name)) - return; + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data *) &st_pll3200c32_cx_0); +} +CLK_OF_DECLARE(c32_pll0, "st,clkgen-pll0", clkgen_c32_pll0_setup); - /* - * PLL 1200MHz output - */ - clk = clkgen_pll_register(parent_name, data, reg, - 0, clk_name, data->lock); +static void __init clkgen_c32_pll1_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data *) &st_pll3200c32_cx_1); +} +CLK_OF_DECLARE(c32_pll1, "st,clkgen-pll1", clkgen_c32_pll1_setup); - if (!IS_ERR(clk)) - of_clk_add_provider(np, of_clk_src_simple_get, clk); +static void __init clkgen_c32_plla9_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data *) &st_pll3200c32_407_a9); +} +CLK_OF_DECLARE(c32_plla9, "st,stih407-clkgen-plla9", clkgen_c32_plla9_setup); - return; +static void __init clkgen_c28_plla9_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data *) &st_pll4600c28_418_a9); } -CLK_OF_DECLARE(clkgengpu_c32_pll, - "st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup); +CLK_OF_DECLARE(c28_plla9, "st,stih418-clkgen-plla9", clkgen_c28_plla9_setup); diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 2afcbd3..254d952 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -1,5 +1,6 @@ config SUNXI_CCU bool "Clock support for Allwinner SoCs" + depends on ARCH_SUNXI || COMPILE_TEST default ARCH_SUNXI if SUNXI_CCU @@ -19,6 +20,10 @@ config SUNXI_CCU_GATE config SUNXI_CCU_MUX bool +config SUNXI_CCU_MULT + bool + select SUNXI_CCU_MUX + config SUNXI_CCU_PHASE bool @@ -51,6 +56,40 @@ config SUNXI_CCU_MP # SoC Drivers +config SUN6I_A31_CCU + bool "Support for the Allwinner A31/A31s CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN6I + +config SUN8I_A23_CCU + bool "Support for the Allwinner A23 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_MULT + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN8I + +config SUN8I_A33_CCU + bool "Support for the Allwinner A33 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_MULT + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN8I + config SUN8I_H3_CCU bool "Support for the Allwinner H3 CCU" select SUNXI_CCU_DIV diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 633ce64..106cba2 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SUNXI_CCU_DIV) += ccu_div.o obj-$(CONFIG_SUNXI_CCU_FRAC) += ccu_frac.o obj-$(CONFIG_SUNXI_CCU_GATE) += ccu_gate.o obj-$(CONFIG_SUNXI_CCU_MUX) += ccu_mux.o +obj-$(CONFIG_SUNXI_CCU_MULT) += ccu_mult.o obj-$(CONFIG_SUNXI_CCU_PHASE) += ccu_phase.o # Multi-factor clocks @@ -17,4 +18,7 @@ obj-$(CONFIG_SUNXI_CCU_NM) += ccu_nm.o obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o # SoC support +obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o +obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o +obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c new file mode 100644 index 0000000..7959646 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -0,0 +1,1239 @@ +/* + * Copyright (c) 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.org> + * + * Based on ccu-sun8i-h3.c by Maxime Ripard. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_mux.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun6i-a31.h" + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_cpu_clk, "pll-cpu", + "osc24M", 0x000, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN6I_A31_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", + "osc24M", 0x010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph", + "osc24M", 0x028, + 8, 5, /* N */ + 4, 2, /* K */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 2, /* post-div */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1", + "osc24M", 0x030, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The MIPI PLL has 2 modes: "MIPI" and "HDMI". + * + * The MIPI mode is a standard NKM-style clock. The HDMI mode is an + * integer / fractional clock with switchable multipliers and dividers. + * This is not supported here. We hardcode the PLL to MIPI mode. + */ +#define SUN6I_A31_PLL_MIPI_REG 0x040 + +static const char * const pll_mipi_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_NKM_WITH_MUX_GATE_LOCK(pll_mipi_clk, "pll-mipi", + pll_mipi_parents, 0x040, + 8, 4, /* N */ + 4, 2, /* K */ + 0, 4, /* M */ + 21, 0, /* mux */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll9_clk, "pll9", + "osc24M", 0x044, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll10_clk, "pll10", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpux_parents[] = { "osc32k", "osc24M", + "pll-cpu", "pll-cpu" }; +static SUNXI_CCU_MUX(cpu_clk, "cpu", cpux_parents, + 0x050, 16, 2, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static struct clk_div_table axi_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 4 }, + { .val = 5, .div = 4 }, + { .val = 6, .div = 4 }, + { .val = 7, .div = 4 }, + { /* Sentinel */ }, +}; + +static SUNXI_CCU_DIV_TABLE(axi_clk, "axi", "cpu", + 0x050, 0, 3, axi_div_table, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi", "pll-periph" }; + +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .variable_prediv = { + .index = 3, + .shift = 6, + .width = 2, + }, + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; + +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph", "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(ahb1_mipidsi_clk, "ahb1-mipidsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(ahb1_ss_clk, "ahb1-ss", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(ahb1_dma_clk, "ahb1-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(ahb1_mmc0_clk, "ahb1-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(ahb1_mmc1_clk, "ahb1-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(ahb1_mmc2_clk, "ahb1-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(ahb1_mmc3_clk, "ahb1-mmc3", "ahb1", + 0x060, BIT(12), 0); +static SUNXI_CCU_GATE(ahb1_nand1_clk, "ahb1-nand1", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(ahb1_nand0_clk, "ahb1-nand0", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(ahb1_sdram_clk, "ahb1-sdram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(ahb1_emac_clk, "ahb1-emac", "ahb1", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(ahb1_ts_clk, "ahb1-ts", "ahb1", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(ahb1_hstimer_clk, "ahb1-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(ahb1_spi0_clk, "ahb1-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(ahb1_spi1_clk, "ahb1-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(ahb1_spi2_clk, "ahb1-spi2", "ahb1", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(ahb1_spi3_clk, "ahb1-spi3", "ahb1", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(ahb1_otg_clk, "ahb1-otg", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(ahb1_ehci0_clk, "ahb1-ehci0", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(ahb1_ehci1_clk, "ahb1-ehci1", "ahb1", + 0x060, BIT(27), 0); +static SUNXI_CCU_GATE(ahb1_ohci0_clk, "ahb1-ohci0", "ahb1", + 0x060, BIT(29), 0); +static SUNXI_CCU_GATE(ahb1_ohci1_clk, "ahb1-ohci1", "ahb1", + 0x060, BIT(30), 0); +static SUNXI_CCU_GATE(ahb1_ohci2_clk, "ahb1-ohci2", "ahb1", + 0x060, BIT(31), 0); + +static SUNXI_CCU_GATE(ahb1_ve_clk, "ahb1-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(ahb1_lcd0_clk, "ahb1-lcd0", "ahb1", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(ahb1_lcd1_clk, "ahb1-lcd1", "ahb1", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(ahb1_csi_clk, "ahb1-csi", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(ahb1_hdmi_clk, "ahb1-hdmi", "ahb1", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(ahb1_be0_clk, "ahb1-be0", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(ahb1_be1_clk, "ahb1-be1", "ahb1", + 0x064, BIT(13), 0); +static SUNXI_CCU_GATE(ahb1_fe0_clk, "ahb1-fe0", "ahb1", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(ahb1_fe1_clk, "ahb1-fe1", "ahb1", + 0x064, BIT(15), 0); +static SUNXI_CCU_GATE(ahb1_mp_clk, "ahb1-mp", "ahb1", + 0x064, BIT(18), 0); +static SUNXI_CCU_GATE(ahb1_gpu_clk, "ahb1-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(ahb1_deu0_clk, "ahb1-deu0", "ahb1", + 0x064, BIT(23), 0); +static SUNXI_CCU_GATE(ahb1_deu1_clk, "ahb1-deu1", "ahb1", + 0x064, BIT(24), 0); +static SUNXI_CCU_GATE(ahb1_drc0_clk, "ahb1-drc0", "ahb1", + 0x064, BIT(25), 0); +static SUNXI_CCU_GATE(ahb1_drc1_clk, "ahb1-drc1", "ahb1", + 0x064, BIT(26), 0); + +static SUNXI_CCU_GATE(apb1_codec_clk, "apb1-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(apb1_spdif_clk, "apb1-spdif", "apb1", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(apb1_digital_mic_clk, "apb1-digital-mic", "apb1", + 0x068, BIT(4), 0); +static SUNXI_CCU_GATE(apb1_pio_clk, "apb1-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(apb1_daudio0_clk, "apb1-daudio0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(apb1_daudio1_clk, "apb1-daudio1", "apb1", + 0x068, BIT(13), 0); + +static SUNXI_CCU_GATE(apb2_i2c0_clk, "apb2-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(apb2_i2c1_clk, "apb2-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(apb2_i2c2_clk, "apb2-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(apb2_i2c3_clk, "apb2-i2c3", "apb2", + 0x06c, BIT(3), 0); +static SUNXI_CCU_GATE(apb2_uart0_clk, "apb2-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(apb2_uart1_clk, "apb2-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(apb2_uart2_clk, "apb2-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(apb2_uart3_clk, "apb2-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(apb2_uart4_clk, "apb2-uart4", "apb2", + 0x06c, BIT(20), 0); +static SUNXI_CCU_GATE(apb2_uart5_clk, "apb2-uart5", "apb2", + 0x06c, BIT(21), 0); + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_clk, "nand0", mod0_default_parents, + 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_clk, "nand1", mod0_default_parents, + 0x084, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, + 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, + 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, + 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, + 0x094, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3_sample", "mmc3", + 0x094, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3_output", "mmc3", + 0x094, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0ac, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const daudio_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(daudio0_clk, "daudio0", daudio_parents, + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(daudio1_clk, "daudio1", daudio_parents, + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", + 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_phy2_clk, "usb-phy2", "osc24M", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", + 0x0cc, BIT(16), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc24M", + 0x0cc, BIT(17), 0); +static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc24M", + 0x0cc, BIT(18), 0); + +/* TODO emac clk not supported yet */ + +static const char * const dram_parents[] = { "pll-ddr", "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mdfs_clk, "mdfs", dram_parents, 0x0f0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static SUNXI_CCU_M_WITH_MUX(sdram0_clk, "sdram0", dram_parents, + 0x0f4, 0, 4, 4, 1, CLK_IS_CRITICAL); +static SUNXI_CCU_M_WITH_MUX(sdram1_clk, "sdram1", dram_parents, + 0x0f4, 8, 4, 12, 1, CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "mdfs", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_isp_clk, "dram-csi-isp", "mdfs", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "mdfs", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0", "mdfs", + 0x100, BIT(16), 0); +static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1", "mdfs", + 0x100, BIT(17), 0); +static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0", "mdfs", + 0x100, BIT(18), 0); +static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1", "mdfs", + 0x100, BIT(19), 0); +static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0", "mdfs", + 0x100, BIT(24), 0); +static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1", "mdfs", + 0x100, BIT(25), 0); +static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0", "mdfs", + 0x100, BIT(26), 0); +static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1", "mdfs", + 0x100, BIT(27), 0); +static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "mdfs", + 0x100, BIT(28), 0); + +static const char * const de_parents[] = { "pll-video0", "pll-video1", + "pll-periph-2x", "pll-gpu", + "pll9", "pll10" }; +static SUNXI_CCU_M_WITH_MUX_GATE(be0_clk, "be0", de_parents, + 0x104, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(be1_clk, "be1", de_parents, + 0x108, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(fe0_clk, "fe0", de_parents, + 0x10c, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(fe1_clk, "fe1", de_parents, + 0x110, 0, 4, 24, 3, BIT(31), 0); + +static const char * const mp_parents[] = { "pll-video0", "pll-video1", + "pll9", "pll10" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mp_clk, "mp", mp_parents, + 0x114, 0, 4, 24, 3, BIT(31), 0); + +static const char * const lcd_ch0_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", + "pll-video1-2x", "pll-mipi" }; +static SUNXI_CCU_MUX_WITH_GATE(lcd0_ch0_clk, "lcd0-ch0", lcd_ch0_parents, + 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(lcd1_ch0_clk, "lcd1-ch0", lcd_ch0_parents, + 0x11c, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const lcd_ch1_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", + "pll-video1-2x" }; +static SUNXI_CCU_M_WITH_MUX_GATE(lcd0_ch1_clk, "lcd0-ch1", lcd_ch1_parents, + 0x12c, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(lcd1_ch1_clk, "lcd1-ch1", lcd_ch1_parents, + 0x12c, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char * const csi_sclk_parents[] = { "pll-video0", "pll-video1", + "pll9", "pll10", "pll-mipi", + "pll-ve" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi0_sclk_clk, "csi0-sclk", csi_sclk_parents, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "pll-video0", "pll-video1", + "osc24M" }; +static const u8 csi_mclk_table[] = { 0, 1, 5 }; +static struct ccu_div csi0_mclk_clk = { + .enable = BIT(15), + .div = _SUNXI_CCU_DIV(0, 4), + .mux = _SUNXI_CCU_MUX_TABLE(8, 3, csi_mclk_table), + .common = { + .reg = 0x134, + .hw.init = CLK_HW_INIT_PARENTS("csi0-mclk", + csi_mclk_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div csi1_mclk_clk = { + .enable = BIT(15), + .div = _SUNXI_CCU_DIV(0, 4), + .mux = _SUNXI_CCU_MUX_TABLE(8, 3, csi_mclk_table), + .common = { + .reg = 0x138, + .hw.init = CLK_HW_INIT_PARENTS("csi1-mclk", + csi_mclk_parents, + &ccu_div_ops, + 0), + }, +}; + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), 0); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); +static SUNXI_CCU_GATE(digital_mic_clk, "digital-mic", "pll-audio", + 0x148, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", lcd_ch1_parents, + 0x150, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", 0x150, BIT(31), 0); + +static SUNXI_CCU_GATE(ps_clk, "ps", "lcd1-ch1", 0x140, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph", + "pll-ddr" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus0_clk, "mbus0", mbus_parents, 0x15c, + 0, 3, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus1_clk, "mbus1", mbus_parents, 0x160, + 0, 3, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static SUNXI_CCU_M_WITH_MUX_GATE(mipi_dsi_clk, "mipi-dsi", lcd_ch1_parents, + 0x168, 16, 3, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(mipi_dsi_dphy_clk, "mipi-dsi-dphy", + lcd_ch1_parents, 0x168, 0, 3, 8, 2, + BIT(15), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(mipi_csi_dphy_clk, "mipi-csi-dphy", + lcd_ch1_parents, 0x16c, 0, 3, 8, 2, + BIT(15), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(iep_drc0_clk, "iep-drc0", de_parents, + 0x180, 0, 3, 24, 2, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(iep_drc1_clk, "iep-drc1", de_parents, + 0x184, 0, 3, 24, 2, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(iep_deu0_clk, "iep-deu0", de_parents, + 0x188, 0, 3, 24, 2, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(iep_deu1_clk, "iep-deu1", de_parents, + 0x18c, 0, 3, 24, 2, BIT(31), 0); + +static const char * const gpu_parents[] = { "pll-gpu", "pll-periph-2x", + "pll-video0", "pll-video1", + "pll9", "pll10" }; +static const struct ccu_mux_fixed_prediv gpu_predivs[] = { + { .index = 1, .div = 3, }, +}; + +static struct ccu_div gpu_core_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV(0, 3), + .mux = { + .shift = 24, + .width = 3, + .fixed_predivs = gpu_predivs, + .n_predivs = ARRAY_SIZE(gpu_predivs), + }, + .common = { + .reg = 0x1a0, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("gpu-core", + gpu_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div gpu_memory_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV(0, 3), + .mux = { + .shift = 24, + .width = 3, + .fixed_predivs = gpu_predivs, + .n_predivs = ARRAY_SIZE(gpu_predivs), + }, + .common = { + .reg = 0x1a4, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("gpu-memory", + gpu_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div gpu_hyd_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV(0, 3), + .mux = { + .shift = 24, + .width = 3, + .fixed_predivs = gpu_predivs, + .n_predivs = ARRAY_SIZE(gpu_predivs), + }, + .common = { + .reg = 0x1a8, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("gpu-hyd", + gpu_parents, + &ccu_div_ops, + 0), + }, +}; + +static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", mod0_default_parents, 0x1b0, + 0, 3, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(trace_clk, "trace", mod0_default_parents, + 0x1b0, + 0, 3, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const clk_out_parents[] = { "osc24M", "osc32k", "osc24M", + "axi", "ahb1" }; +static const u8 clk_out_table[] = { 0, 1, 2, 11, 13 }; + +static const struct ccu_mux_fixed_prediv clk_out_predivs[] = { + { .index = 0, .div = 750, }, + { .index = 3, .div = 4, }, + { .index = 4, .div = 4, }, +}; + +static struct ccu_mp out_a_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 4, + .table = clk_out_table, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x300, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-a", + clk_out_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_mp out_b_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 4, + .table = clk_out_table, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x304, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-b", + clk_out_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_mp out_c_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 4, + .table = clk_out_table, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x308, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-c", + clk_out_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_common *sun6i_a31_ccu_clks[] = { + &pll_cpu_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr_clk.common, + &pll_periph_clk.common, + &pll_video1_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll9_clk.common, + &pll10_clk.common, + &cpu_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &ahb1_mipidsi_clk.common, + &ahb1_ss_clk.common, + &ahb1_dma_clk.common, + &ahb1_mmc0_clk.common, + &ahb1_mmc1_clk.common, + &ahb1_mmc2_clk.common, + &ahb1_mmc3_clk.common, + &ahb1_nand1_clk.common, + &ahb1_nand0_clk.common, + &ahb1_sdram_clk.common, + &ahb1_emac_clk.common, + &ahb1_ts_clk.common, + &ahb1_hstimer_clk.common, + &ahb1_spi0_clk.common, + &ahb1_spi1_clk.common, + &ahb1_spi2_clk.common, + &ahb1_spi3_clk.common, + &ahb1_otg_clk.common, + &ahb1_ehci0_clk.common, + &ahb1_ehci1_clk.common, + &ahb1_ohci0_clk.common, + &ahb1_ohci1_clk.common, + &ahb1_ohci2_clk.common, + &ahb1_ve_clk.common, + &ahb1_lcd0_clk.common, + &ahb1_lcd1_clk.common, + &ahb1_csi_clk.common, + &ahb1_hdmi_clk.common, + &ahb1_be0_clk.common, + &ahb1_be1_clk.common, + &ahb1_fe0_clk.common, + &ahb1_fe1_clk.common, + &ahb1_mp_clk.common, + &ahb1_gpu_clk.common, + &ahb1_deu0_clk.common, + &ahb1_deu1_clk.common, + &ahb1_drc0_clk.common, + &ahb1_drc1_clk.common, + &apb1_codec_clk.common, + &apb1_spdif_clk.common, + &apb1_digital_mic_clk.common, + &apb1_pio_clk.common, + &apb1_daudio0_clk.common, + &apb1_daudio1_clk.common, + &apb2_i2c0_clk.common, + &apb2_i2c1_clk.common, + &apb2_i2c2_clk.common, + &apb2_i2c3_clk.common, + &apb2_uart0_clk.common, + &apb2_uart1_clk.common, + &apb2_uart2_clk.common, + &apb2_uart3_clk.common, + &apb2_uart4_clk.common, + &apb2_uart5_clk.common, + &nand0_clk.common, + &nand1_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &mmc3_clk.common, + &mmc3_sample_clk.common, + &mmc3_output_clk.common, + &ts_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &spi3_clk.common, + &daudio0_clk.common, + &daudio1_clk.common, + &spdif_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_phy2_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &usb_ohci2_clk.common, + &mdfs_clk.common, + &sdram0_clk.common, + &sdram1_clk.common, + &dram_ve_clk.common, + &dram_csi_isp_clk.common, + &dram_ts_clk.common, + &dram_drc0_clk.common, + &dram_drc1_clk.common, + &dram_deu0_clk.common, + &dram_deu1_clk.common, + &dram_fe0_clk.common, + &dram_fe1_clk.common, + &dram_be0_clk.common, + &dram_be1_clk.common, + &dram_mp_clk.common, + &be0_clk.common, + &be1_clk.common, + &fe0_clk.common, + &fe1_clk.common, + &mp_clk.common, + &lcd0_ch0_clk.common, + &lcd1_ch0_clk.common, + &lcd0_ch1_clk.common, + &lcd1_ch1_clk.common, + &csi0_sclk_clk.common, + &csi0_mclk_clk.common, + &csi1_mclk_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &digital_mic_clk.common, + &hdmi_clk.common, + &hdmi_ddc_clk.common, + &ps_clk.common, + &mbus0_clk.common, + &mbus1_clk.common, + &mipi_dsi_clk.common, + &mipi_dsi_dphy_clk.common, + &mipi_csi_dphy_clk.common, + &iep_drc0_clk.common, + &iep_drc1_clk.common, + &iep_deu0_clk.common, + &iep_deu1_clk.common, + &gpu_core_clk.common, + &gpu_memory_clk.common, + &gpu_hyd_clk.common, + &ats_clk.common, + &trace_clk.common, + &out_a_clk.common, + &out_b_clk.common, + &out_c_clk.common, +}; + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x", + "pll-periph", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, CLK_SET_RATE_PARENT); + +static struct clk_hw_onecell_data sun6i_a31_hw_clks = { + .hws = { + [CLK_PLL_CPU] = &pll_cpu_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL9] = &pll9_clk.common.hw, + [CLK_PLL10] = &pll10_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_AHB1_MIPIDSI] = &ahb1_mipidsi_clk.common.hw, + [CLK_AHB1_SS] = &ahb1_ss_clk.common.hw, + [CLK_AHB1_DMA] = &ahb1_dma_clk.common.hw, + [CLK_AHB1_MMC0] = &ahb1_mmc0_clk.common.hw, + [CLK_AHB1_MMC1] = &ahb1_mmc1_clk.common.hw, + [CLK_AHB1_MMC2] = &ahb1_mmc2_clk.common.hw, + [CLK_AHB1_MMC3] = &ahb1_mmc3_clk.common.hw, + [CLK_AHB1_NAND1] = &ahb1_nand1_clk.common.hw, + [CLK_AHB1_NAND0] = &ahb1_nand0_clk.common.hw, + [CLK_AHB1_SDRAM] = &ahb1_sdram_clk.common.hw, + [CLK_AHB1_EMAC] = &ahb1_emac_clk.common.hw, + [CLK_AHB1_TS] = &ahb1_ts_clk.common.hw, + [CLK_AHB1_HSTIMER] = &ahb1_hstimer_clk.common.hw, + [CLK_AHB1_SPI0] = &ahb1_spi0_clk.common.hw, + [CLK_AHB1_SPI1] = &ahb1_spi1_clk.common.hw, + [CLK_AHB1_SPI2] = &ahb1_spi2_clk.common.hw, + [CLK_AHB1_SPI3] = &ahb1_spi3_clk.common.hw, + [CLK_AHB1_OTG] = &ahb1_otg_clk.common.hw, + [CLK_AHB1_EHCI0] = &ahb1_ehci0_clk.common.hw, + [CLK_AHB1_EHCI1] = &ahb1_ehci1_clk.common.hw, + [CLK_AHB1_OHCI0] = &ahb1_ohci0_clk.common.hw, + [CLK_AHB1_OHCI1] = &ahb1_ohci1_clk.common.hw, + [CLK_AHB1_OHCI2] = &ahb1_ohci2_clk.common.hw, + [CLK_AHB1_VE] = &ahb1_ve_clk.common.hw, + [CLK_AHB1_LCD0] = &ahb1_lcd0_clk.common.hw, + [CLK_AHB1_LCD1] = &ahb1_lcd1_clk.common.hw, + [CLK_AHB1_CSI] = &ahb1_csi_clk.common.hw, + [CLK_AHB1_HDMI] = &ahb1_hdmi_clk.common.hw, + [CLK_AHB1_BE0] = &ahb1_be0_clk.common.hw, + [CLK_AHB1_BE1] = &ahb1_be1_clk.common.hw, + [CLK_AHB1_FE0] = &ahb1_fe0_clk.common.hw, + [CLK_AHB1_FE1] = &ahb1_fe1_clk.common.hw, + [CLK_AHB1_MP] = &ahb1_mp_clk.common.hw, + [CLK_AHB1_GPU] = &ahb1_gpu_clk.common.hw, + [CLK_AHB1_DEU0] = &ahb1_deu0_clk.common.hw, + [CLK_AHB1_DEU1] = &ahb1_deu1_clk.common.hw, + [CLK_AHB1_DRC0] = &ahb1_drc0_clk.common.hw, + [CLK_AHB1_DRC1] = &ahb1_drc1_clk.common.hw, + [CLK_APB1_CODEC] = &apb1_codec_clk.common.hw, + [CLK_APB1_SPDIF] = &apb1_spdif_clk.common.hw, + [CLK_APB1_DIGITAL_MIC] = &apb1_digital_mic_clk.common.hw, + [CLK_APB1_PIO] = &apb1_pio_clk.common.hw, + [CLK_APB1_DAUDIO0] = &apb1_daudio0_clk.common.hw, + [CLK_APB1_DAUDIO1] = &apb1_daudio1_clk.common.hw, + [CLK_APB2_I2C0] = &apb2_i2c0_clk.common.hw, + [CLK_APB2_I2C1] = &apb2_i2c1_clk.common.hw, + [CLK_APB2_I2C2] = &apb2_i2c2_clk.common.hw, + [CLK_APB2_I2C3] = &apb2_i2c3_clk.common.hw, + [CLK_APB2_UART0] = &apb2_uart0_clk.common.hw, + [CLK_APB2_UART1] = &apb2_uart1_clk.common.hw, + [CLK_APB2_UART2] = &apb2_uart2_clk.common.hw, + [CLK_APB2_UART3] = &apb2_uart3_clk.common.hw, + [CLK_APB2_UART4] = &apb2_uart4_clk.common.hw, + [CLK_APB2_UART5] = &apb2_uart5_clk.common.hw, + [CLK_NAND0] = &nand0_clk.common.hw, + [CLK_NAND1] = &nand1_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw, + [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_DAUDIO0] = &daudio0_clk.common.hw, + [CLK_DAUDIO1] = &daudio1_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_PHY2] = &usb_phy2_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, + [CLK_MDFS] = &mdfs_clk.common.hw, + [CLK_SDRAM0] = &sdram0_clk.common.hw, + [CLK_SDRAM1] = &sdram1_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI_ISP] = &dram_csi_isp_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw, + [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw, + [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw, + [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw, + [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw, + [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw, + [CLK_DRAM_BE0] = &dram_be0_clk.common.hw, + [CLK_DRAM_BE1] = &dram_be1_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_BE0] = &be0_clk.common.hw, + [CLK_BE1] = &be1_clk.common.hw, + [CLK_FE0] = &fe0_clk.common.hw, + [CLK_FE1] = &fe1_clk.common.hw, + [CLK_MP] = &mp_clk.common.hw, + [CLK_LCD0_CH0] = &lcd0_ch0_clk.common.hw, + [CLK_LCD1_CH0] = &lcd1_ch0_clk.common.hw, + [CLK_LCD0_CH1] = &lcd0_ch1_clk.common.hw, + [CLK_LCD1_CH1] = &lcd1_ch1_clk.common.hw, + [CLK_CSI0_SCLK] = &csi0_sclk_clk.common.hw, + [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, + [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_DIGITAL_MIC] = &digital_mic_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_DDC] = &hdmi_ddc_clk.common.hw, + [CLK_PS] = &ps_clk.common.hw, + [CLK_MBUS0] = &mbus0_clk.common.hw, + [CLK_MBUS1] = &mbus1_clk.common.hw, + [CLK_MIPI_DSI] = &mipi_dsi_clk.common.hw, + [CLK_MIPI_DSI_DPHY] = &mipi_dsi_dphy_clk.common.hw, + [CLK_MIPI_CSI_DPHY] = &mipi_csi_dphy_clk.common.hw, + [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw, + [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw, + [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw, + [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw, + [CLK_GPU_CORE] = &gpu_core_clk.common.hw, + [CLK_GPU_MEMORY] = &gpu_memory_clk.common.hw, + [CLK_GPU_HYD] = &gpu_hyd_clk.common.hw, + [CLK_ATS] = &ats_clk.common.hw, + [CLK_TRACE] = &trace_clk.common.hw, + [CLK_OUT_A] = &out_a_clk.common.hw, + [CLK_OUT_B] = &out_b_clk.common.hw, + [CLK_OUT_C] = &out_c_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun6i_a31_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_PHY2] = { 0x0cc, BIT(2) }, + + [RST_AHB1_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_AHB1_SS] = { 0x2c0, BIT(5) }, + [RST_AHB1_DMA] = { 0x2c0, BIT(6) }, + [RST_AHB1_MMC0] = { 0x2c0, BIT(8) }, + [RST_AHB1_MMC1] = { 0x2c0, BIT(9) }, + [RST_AHB1_MMC2] = { 0x2c0, BIT(10) }, + [RST_AHB1_MMC3] = { 0x2c0, BIT(11) }, + [RST_AHB1_NAND1] = { 0x2c0, BIT(12) }, + [RST_AHB1_NAND0] = { 0x2c0, BIT(13) }, + [RST_AHB1_SDRAM] = { 0x2c0, BIT(14) }, + [RST_AHB1_EMAC] = { 0x2c0, BIT(17) }, + [RST_AHB1_TS] = { 0x2c0, BIT(18) }, + [RST_AHB1_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_AHB1_SPI0] = { 0x2c0, BIT(20) }, + [RST_AHB1_SPI1] = { 0x2c0, BIT(21) }, + [RST_AHB1_SPI2] = { 0x2c0, BIT(22) }, + [RST_AHB1_SPI3] = { 0x2c0, BIT(23) }, + [RST_AHB1_OTG] = { 0x2c0, BIT(24) }, + [RST_AHB1_EHCI0] = { 0x2c0, BIT(26) }, + [RST_AHB1_EHCI1] = { 0x2c0, BIT(27) }, + [RST_AHB1_OHCI0] = { 0x2c0, BIT(29) }, + [RST_AHB1_OHCI1] = { 0x2c0, BIT(30) }, + [RST_AHB1_OHCI2] = { 0x2c0, BIT(31) }, + + [RST_AHB1_VE] = { 0x2c4, BIT(0) }, + [RST_AHB1_LCD0] = { 0x2c4, BIT(4) }, + [RST_AHB1_LCD1] = { 0x2c4, BIT(5) }, + [RST_AHB1_CSI] = { 0x2c4, BIT(8) }, + [RST_AHB1_HDMI] = { 0x2c4, BIT(11) }, + [RST_AHB1_BE0] = { 0x2c4, BIT(12) }, + [RST_AHB1_BE1] = { 0x2c4, BIT(13) }, + [RST_AHB1_FE0] = { 0x2c4, BIT(14) }, + [RST_AHB1_FE1] = { 0x2c4, BIT(15) }, + [RST_AHB1_MP] = { 0x2c4, BIT(18) }, + [RST_AHB1_GPU] = { 0x2c4, BIT(20) }, + [RST_AHB1_DEU0] = { 0x2c4, BIT(23) }, + [RST_AHB1_DEU1] = { 0x2c4, BIT(24) }, + [RST_AHB1_DRC0] = { 0x2c4, BIT(25) }, + [RST_AHB1_DRC1] = { 0x2c4, BIT(26) }, + [RST_AHB1_LVDS] = { 0x2c8, BIT(0) }, + + [RST_APB1_CODEC] = { 0x2d0, BIT(0) }, + [RST_APB1_SPDIF] = { 0x2d0, BIT(1) }, + [RST_APB1_DIGITAL_MIC] = { 0x2d0, BIT(4) }, + [RST_APB1_DAUDIO0] = { 0x2d0, BIT(12) }, + [RST_APB1_DAUDIO1] = { 0x2d0, BIT(13) }, + + [RST_APB2_I2C0] = { 0x2d8, BIT(0) }, + [RST_APB2_I2C1] = { 0x2d8, BIT(1) }, + [RST_APB2_I2C2] = { 0x2d8, BIT(2) }, + [RST_APB2_I2C3] = { 0x2d8, BIT(3) }, + [RST_APB2_UART0] = { 0x2d8, BIT(16) }, + [RST_APB2_UART1] = { 0x2d8, BIT(17) }, + [RST_APB2_UART2] = { 0x2d8, BIT(18) }, + [RST_APB2_UART3] = { 0x2d8, BIT(19) }, + [RST_APB2_UART4] = { 0x2d8, BIT(20) }, + [RST_APB2_UART5] = { 0x2d8, BIT(21) }, +}; + +static const struct sunxi_ccu_desc sun6i_a31_ccu_desc = { + .ccu_clks = sun6i_a31_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun6i_a31_ccu_clks), + + .hw_clks = &sun6i_a31_hw_clks, + + .resets = sun6i_a31_ccu_resets, + .num_resets = ARRAY_SIZE(sun6i_a31_ccu_resets), +}; + +static struct ccu_mux_nb sun6i_a31_cpu_nb = { + .common = &cpu_clk.common, + .cm = &cpu_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + +static void __init sun6i_a31_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN6I_A31_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN6I_A31_PLL_AUDIO_REG); + + /* Force PLL-MIPI to MIPI mode */ + val = readl(reg + SUN6I_A31_PLL_MIPI_REG); + val &= BIT(16); + writel(val, reg + SUN6I_A31_PLL_MIPI_REG); + + sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc); + + ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, + &sun6i_a31_cpu_nb); +} +CLK_OF_DECLARE(sun6i_a31_ccu, "allwinner,sun6i-a31-ccu", + sun6i_a31_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h new file mode 100644 index 0000000..4e43401 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h @@ -0,0 +1,72 @@ +/* + * Copyright 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.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. + */ + +#ifndef _CCU_SUN6I_A31_H_ +#define _CCU_SUN6I_A31_H_ + +#include <dt-bindings/clock/sun6i-a31-ccu.h> +#include <dt-bindings/reset/sun6i-a31-ccu.h> + +#define CLK_PLL_CPU 0 +#define CLK_PLL_AUDIO_BASE 1 +#define CLK_PLL_AUDIO 2 +#define CLK_PLL_AUDIO_2X 3 +#define CLK_PLL_AUDIO_4X 4 +#define CLK_PLL_AUDIO_8X 5 +#define CLK_PLL_VIDEO0 6 +#define CLK_PLL_VIDEO0_2X 7 +#define CLK_PLL_VE 8 +#define CLK_PLL_DDR 9 + +/* The PLL_PERIPH clock is exported */ + +#define CLK_PLL_PERIPH_2X 11 +#define CLK_PLL_VIDEO1 12 +#define CLK_PLL_VIDEO1_2X 13 +#define CLK_PLL_GPU 14 +#define CLK_PLL_MIPI 15 +#define CLK_PLL9 16 +#define CLK_PLL10 17 + +/* The CPUX clock is exported */ + +#define CLK_AXI 19 +#define CLK_AHB1 20 +#define CLK_APB1 21 +#define CLK_APB2 22 + +/* All the bus gates are exported */ + +/* The first bunch of module clocks are exported */ + +/* EMAC clock is not implemented */ + +#define CLK_MDFS 107 +#define CLK_SDRAM0 108 +#define CLK_SDRAM1 109 + +/* All the DRAM gates are exported */ + +/* Some more module clocks are exported */ + +#define CLK_MBUS0 141 +#define CLK_MBUS1 142 + +/* Some more module clocks and external clock outputs are exported */ + +#define CLK_NUMBER (CLK_OUT_C + 1) + +#endif /* _CCU_SUN6I_A31_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h b/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h new file mode 100644 index 0000000..62c0f8d --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * 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 _CCU_SUN8I_A23_A33_H_ +#define _CCU_SUN8I_A23_A33_H_ + +#include <dt-bindings/clock/sun8i-a23-a33-ccu.h> +#include <dt-bindings/reset/sun8i-a23-a33-ccu.h> + +#define CLK_PLL_CPUX 0 +#define CLK_PLL_AUDIO_BASE 1 +#define CLK_PLL_AUDIO 2 +#define CLK_PLL_AUDIO_2X 3 +#define CLK_PLL_AUDIO_4X 4 +#define CLK_PLL_AUDIO_8X 5 +#define CLK_PLL_VIDEO 6 +#define CLK_PLL_VIDEO_2X 7 +#define CLK_PLL_VE 8 +#define CLK_PLL_DDR0 9 +#define CLK_PLL_PERIPH 10 +#define CLK_PLL_PERIPH_2X 11 +#define CLK_PLL_GPU 12 +#define CLK_PLL_MIPI 13 +#define CLK_PLL_HSIC 14 +#define CLK_PLL_DE 15 +#define CLK_PLL_DDR1 16 +#define CLK_PLL_DDR 17 + +/* The CPUX clock is exported */ + +#define CLK_AXI 19 +#define CLK_AHB1 20 +#define CLK_APB1 21 +#define CLK_APB2 22 + +/* All the bus gates are exported */ + +/* The first part of the mod clocks is exported */ + +#define CLK_DRAM 79 + +/* Some more module clocks are exported */ + +#define CLK_MBUS 95 + +/* And the last module clocks are exported */ + +#define CLK_NUMBER (CLK_ATS + 1) + +#endif /* _CCU_SUN8I_A23_A33_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c new file mode 100644 index 0000000..2646d98 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2016 Maxime Ripard. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-a23-a33.h" + + +static struct ccu_nkmp pll_cpux_clk = { + .enable = BIT(31), + .lock = BIT(28), + + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV_MAX(16, 2, 4), + + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-cpux", "osc24M", + &ccu_nkmp_ops, + 0), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN8I_A23_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", + "osc24M", 0x010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph", + "osc24M", 0x028, + 8, 5, /* N */ + 4, 2, /* K */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 2, /* post-div */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The MIPI PLL has 2 modes: "MIPI" and "HDMI". + * + * The MIPI mode is a standard NKM-style clock. The HDMI mode is an + * integer / fractional clock with switchable multipliers and dividers. + * This is not supported here. We hardcode the PLL to MIPI mode. + */ +#define SUN8I_A23_PLL_MIPI_REG 0x040 +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi", + "pll-video", 0x040, + 8, 4, /* N */ + 4, 2, /* K */ + 0, 4, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic", + "osc24M", 0x044, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpux_parents[] = { "osc32k", "osc24M", + "pll-cpux" , "pll-cpux" }; +static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, + 0x050, 16, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi" , "pll-periph" }; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .variable_prediv = { + .index = 3, + .shift = 6, + .width = 2, + }, + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph" , "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_ehci_clk, "bus-ehci", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ohci_clk, "bus-ohci", "ahb1", + 0x060, BIT(29), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_lcd_clk, "bus-lcd", "ahb1", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_de_be_clk, "bus-de-be", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(bus_de_fe_clk, "bus-de-fe", "ahb1", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1", + 0x064, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1", + 0x064, BIT(22), 0); +static SUNXI_CCU_GATE(bus_drc_clk, "bus-drc", "ahb1", + 0x064, BIT(25), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", + 0x068, BIT(13), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2", + 0x06c, BIT(20), 0); + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, + 0x0b0, 16, 2, BIT(31), 0); + +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, + 0x0b4, 16, 2, BIT(31), 0); + +/* TODO: the parent for most of the USB clocks is not known */ +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_hsic_12M_clk, "usb-hsic-12M", "osc24M", + 0x0cc, BIT(11), 0); +static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "osc24M", + 0x0cc, BIT(16), 0); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "pll-ddr", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_drc_clk, "dram-drc", "pll-ddr", + 0x100, BIT(16), 0); +static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "pll-ddr", + 0x100, BIT(24), 0); +static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "pll-ddr", + 0x100, BIT(26), 0); + +static const char * const de_parents[] = { "pll-video", "pll-periph-2x", + "pll-gpu", "pll-de" }; +static const u8 de_table[] = { 0, 2, 3, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_be_clk, "de-be", + de_parents, de_table, + 0x104, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_fe_clk, "de-fe", + de_parents, de_table, + 0x10c, 0, 4, 24, 3, BIT(31), 0); + +static const char * const lcd_ch0_parents[] = { "pll-video", "pll-video-2x", + "pll-mipi" }; +static const u8 lcd_ch0_table[] = { 0, 2, 4 }; +static SUNXI_CCU_MUX_TABLE_WITH_GATE(lcd_ch0_clk, "lcd-ch0", + lcd_ch0_parents, lcd_ch0_table, + 0x118, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char * const lcd_ch1_parents[] = { "pll-video", "pll-video-2x" }; +static const u8 lcd_ch1_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd_ch1_clk, "lcd-ch1", + lcd_ch1_parents, lcd_ch1_table, + 0x12c, 0, 4, 24, 2, BIT(31), 0); + +static const char * const csi_sclk_parents[] = { "pll-video", "pll-de", + "pll-mipi", "pll-ve" }; +static const u8 csi_sclk_table[] = { 0, 3, 4, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_sclk_clk, "csi-sclk", + csi_sclk_parents, csi_sclk_table, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "pll-video", "pll-de", + "osc24M" }; +static const u8 csi_mclk_table[] = { 0, 3, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk", + csi_mclk_parents, csi_mclk_table, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", + 0x140, BIT(31), 0); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph-2x", + "pll-ddr" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); + +static const char * const dsi_sclk_parents[] = { "pll-video", "pll-video-2x" }; +static const u8 dsi_sclk_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_sclk_clk, "dsi-sclk", + dsi_sclk_parents, dsi_sclk_table, + 0x168, 16, 4, 24, 2, BIT(31), 0); + +static const char * const dsi_dphy_parents[] = { "pll-video", "pll-periph" }; +static const u8 dsi_dphy_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy", + dsi_dphy_parents, dsi_dphy_table, + 0x168, 0, 4, 8, 2, BIT(15), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc", + de_parents, de_table, + 0x180, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", + 0x1a0, 0, 3, BIT(31), 0); + +static const char * const ats_parents[] = { "osc24M", "pll-periph" }; +static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents, + 0x1b0, 0, 3, 24, 2, BIT(31), 0); + +static struct ccu_common *sun8i_a23_ccu_clks[] = { + &pll_cpux_clk.common, + &pll_audio_base_clk.common, + &pll_video_clk.common, + &pll_ve_clk.common, + &pll_ddr_clk.common, + &pll_periph_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll_hsic_clk.common, + &pll_de_clk.common, + &cpux_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_nand_clk.common, + &bus_dram_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_otg_clk.common, + &bus_ehci_clk.common, + &bus_ohci_clk.common, + &bus_ve_clk.common, + &bus_lcd_clk.common, + &bus_csi_clk.common, + &bus_de_fe_clk.common, + &bus_de_be_clk.common, + &bus_gpu_clk.common, + &bus_msgbox_clk.common, + &bus_spinlock_clk.common, + &bus_drc_clk.common, + &bus_codec_clk.common, + &bus_pio_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_hsic_clk.common, + &usb_hsic_12M_clk.common, + &usb_ohci_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &dram_drc_clk.common, + &dram_de_fe_clk.common, + &dram_de_be_clk.common, + &de_be_clk.common, + &de_fe_clk.common, + &lcd_ch0_clk.common, + &lcd_ch1_clk.common, + &csi_sclk_clk.common, + &csi_mclk_clk.common, + &ve_clk.common, + &ac_dig_clk.common, + &avs_clk.common, + &mbus_clk.common, + &dsi_sclk_clk.common, + &dsi_dphy_clk.common, + &drc_clk.common, + &gpu_clk.common, + &ats_clk.common, +}; + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x", + "pll-periph", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video_2x_clk, "pll-video-2x", + "pll-video", 1, 2, 0); + +static struct clk_hw_onecell_data sun8i_a23_hw_clks = { + .hws = { + [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO] = &pll_video_clk.common.hw, + [CLK_PLL_VIDEO_2X] = &pll_video_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_CPUX] = &cpux_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_NAND] = &bus_nand_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI] = &bus_ehci_clk.common.hw, + [CLK_BUS_OHCI] = &bus_ohci_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_LCD] = &bus_lcd_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_BUS_DE_BE] = &bus_de_be_clk.common.hw, + [CLK_BUS_DE_FE] = &bus_de_fe_clk.common.hw, + [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, + [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw, + [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, + [CLK_BUS_DRC] = &bus_drc_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_HSIC] = &usb_hsic_clk.common.hw, + [CLK_USB_HSIC_12M] = &usb_hsic_12M_clk.common.hw, + [CLK_USB_OHCI] = &usb_ohci_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_DRC] = &dram_drc_clk.common.hw, + [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw, + [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw, + [CLK_DE_BE] = &de_be_clk.common.hw, + [CLK_DE_FE] = &de_fe_clk.common.hw, + [CLK_LCD_CH0] = &lcd_ch0_clk.common.hw, + [CLK_LCD_CH1] = &lcd_ch1_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AC_DIG] = &ac_dig_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_DSI_SCLK] = &dsi_sclk_clk.common.hw, + [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw, + [CLK_DRC] = &drc_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_ATS] = &ats_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_a23_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_HSIC] = { 0x0cc, BIT(2) }, + + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_NAND] = { 0x2c0, BIT(13) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_OTG] = { 0x2c0, BIT(24) }, + [RST_BUS_EHCI] = { 0x2c0, BIT(26) }, + [RST_BUS_OHCI] = { 0x2c0, BIT(29) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_LCD] = { 0x2c4, BIT(4) }, + [RST_BUS_CSI] = { 0x2c4, BIT(8) }, + [RST_BUS_DE_BE] = { 0x2c4, BIT(12) }, + [RST_BUS_DE_FE] = { 0x2c4, BIT(14) }, + [RST_BUS_GPU] = { 0x2c4, BIT(20) }, + [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) }, + [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) }, + [RST_BUS_DRC] = { 0x2c4, BIT(25) }, + + [RST_BUS_LVDS] = { 0x2c8, BIT(0) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, + [RST_BUS_UART3] = { 0x2d8, BIT(19) }, + [RST_BUS_UART4] = { 0x2d8, BIT(20) }, +}; + +static const struct sunxi_ccu_desc sun8i_a23_ccu_desc = { + .ccu_clks = sun8i_a23_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a23_ccu_clks), + + .hw_clks = &sun8i_a23_hw_clks, + + .resets = sun8i_a23_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_a23_ccu_resets), +}; + +static void __init sun8i_a23_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN8I_A23_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN8I_A23_PLL_AUDIO_REG); + + /* Force PLL-MIPI to MIPI mode */ + val = readl(reg + SUN8I_A23_PLL_MIPI_REG); + val &= ~BIT(16); + writel(val, reg + SUN8I_A23_PLL_MIPI_REG); + + sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc); +} +CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu", + sun8i_a23_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c new file mode 100644 index 0000000..96b40ca --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2016 Maxime Ripard. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-a23-a33.h" + +static struct ccu_nkmp pll_cpux_clk = { + .enable = BIT(31), + .lock = BIT(28), + + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV_MAX(16, 2, 4), + + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-cpux", "osc24M", + &ccu_nkmp_ops, + 0), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN8I_A33_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", + "osc24M", 0x010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph", + "osc24M", 0x028, + 8, 5, /* N */ + 4, 2, /* K */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 2, /* post-div */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The MIPI PLL has 2 modes: "MIPI" and "HDMI". + * + * The MIPI mode is a standard NKM-style clock. The HDMI mode is an + * integer / fractional clock with switchable multipliers and dividers. + * This is not supported here. We hardcode the PLL to MIPI mode. + */ +#define SUN8I_A33_PLL_MIPI_REG 0x040 +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi", + "pll-video", 0x040, + 8, 4, /* N */ + 4, 2, /* K */ + 0, 4, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic", + "osc24M", 0x044, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: Fix N */ +static SUNXI_CCU_N_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", + "osc24M", 0x04c, + 8, 6, /* N */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpux_parents[] = { "osc32k", "osc24M", + "pll-cpux" , "pll-cpux" }; +static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, + 0x050, 16, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi" , "pll-periph" }; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .variable_prediv = { + .index = 3, + .shift = 6, + .width = 2, + }, + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph" , "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ss_clk, "bus-ss", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_ehci_clk, "bus-ehci", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ohci_clk, "bus-ohci", "ahb1", + 0x060, BIT(29), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_lcd_clk, "bus-lcd", "ahb1", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_de_be_clk, "bus-de-be", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(bus_de_fe_clk, "bus-de-fe", "ahb1", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1", + 0x064, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1", + 0x064, BIT(22), 0); +static SUNXI_CCU_GATE(bus_drc_clk, "bus-drc", "ahb1", + 0x064, BIT(25), 0); +static SUNXI_CCU_GATE(bus_sat_clk, "bus-sat", "ahb1", + 0x064, BIT(26), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", + 0x068, BIT(13), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2", + 0x06c, BIT(20), 0); + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, + 0x0b0, 16, 2, BIT(31), 0); + +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, + 0x0b4, 16, 2, BIT(31), 0); + +/* TODO: the parent for most of the USB clocks is not known */ +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_hsic_12M_clk, "usb-hsic-12M", "osc24M", + 0x0cc, BIT(11), 0); +static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "osc24M", + 0x0cc, BIT(16), 0); + +static SUNXI_CCU_M(dram_clk, "dram", "pll-ddr", + 0x0f4, 0, 4, CLK_IS_CRITICAL); + +static const char * const pll_ddr_parents[] = { "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_MUX(pll_ddr_clk, "pll-ddr", pll_ddr_parents, + 0x0f8, 16, 1, 0); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_drc_clk, "dram-drc", "dram", + 0x100, BIT(16), 0); +static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "dram", + 0x100, BIT(24), 0); +static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "dram", + 0x100, BIT(26), 0); + +static const char * const de_parents[] = { "pll-video", "pll-periph-2x", + "pll-gpu", "pll-de" }; +static const u8 de_table[] = { 0, 2, 3, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_be_clk, "de-be", + de_parents, de_table, + 0x104, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_fe_clk, "de-fe", + de_parents, de_table, + 0x10c, 0, 4, 24, 3, BIT(31), 0); + +static const char * const lcd_ch0_parents[] = { "pll-video", "pll-video-2x", + "pll-mipi" }; +static const u8 lcd_ch0_table[] = { 0, 2, 4 }; +static SUNXI_CCU_MUX_TABLE_WITH_GATE(lcd_ch0_clk, "lcd-ch0", + lcd_ch0_parents, lcd_ch0_table, + 0x118, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char * const lcd_ch1_parents[] = { "pll-video", "pll-video-2x" }; +static const u8 lcd_ch1_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd_ch1_clk, "lcd-ch1", + lcd_ch1_parents, lcd_ch1_table, + 0x12c, 0, 4, 24, 2, BIT(31), 0); + +static const char * const csi_sclk_parents[] = { "pll-video", "pll-de", + "pll-mipi", "pll-ve" }; +static const u8 csi_sclk_table[] = { 0, 3, 4, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_sclk_clk, "csi-sclk", + csi_sclk_parents, csi_sclk_table, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "pll-video", "pll-de", + "osc24M" }; +static const u8 csi_mclk_table[] = { 0, 3, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk", + csi_mclk_parents, csi_mclk_table, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", + 0x140, BIT(31), 0); +static SUNXI_CCU_GATE(ac_dig_4x_clk, "ac-dig-4x", "pll-audio-4x", + 0x140, BIT(30), 0); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph-2x", + "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); + +static const char * const dsi_sclk_parents[] = { "pll-video", "pll-video-2x" }; +static const u8 dsi_sclk_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_sclk_clk, "dsi-sclk", + dsi_sclk_parents, dsi_sclk_table, + 0x168, 16, 4, 24, 2, BIT(31), 0); + +static const char * const dsi_dphy_parents[] = { "pll-video", "pll-periph" }; +static const u8 dsi_dphy_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy", + dsi_dphy_parents, dsi_dphy_table, + 0x168, 0, 4, 8, 2, BIT(15), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc", + de_parents, de_table, + 0x180, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", + 0x1a0, 0, 3, BIT(31), 0); + +static const char * const ats_parents[] = { "osc24M", "pll-periph" }; +static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents, + 0x1b0, 0, 3, 24, 2, BIT(31), 0); + +static struct ccu_common *sun8i_a33_ccu_clks[] = { + &pll_cpux_clk.common, + &pll_audio_base_clk.common, + &pll_video_clk.common, + &pll_ve_clk.common, + &pll_ddr0_clk.common, + &pll_periph_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll_hsic_clk.common, + &pll_de_clk.common, + &pll_ddr1_clk.common, + &pll_ddr_clk.common, + &cpux_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_ss_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_nand_clk.common, + &bus_dram_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_otg_clk.common, + &bus_ehci_clk.common, + &bus_ohci_clk.common, + &bus_ve_clk.common, + &bus_lcd_clk.common, + &bus_csi_clk.common, + &bus_de_fe_clk.common, + &bus_de_be_clk.common, + &bus_gpu_clk.common, + &bus_msgbox_clk.common, + &bus_spinlock_clk.common, + &bus_drc_clk.common, + &bus_sat_clk.common, + &bus_codec_clk.common, + &bus_pio_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_hsic_clk.common, + &usb_hsic_12M_clk.common, + &usb_ohci_clk.common, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &dram_drc_clk.common, + &dram_de_fe_clk.common, + &dram_de_be_clk.common, + &de_be_clk.common, + &de_fe_clk.common, + &lcd_ch0_clk.common, + &lcd_ch1_clk.common, + &csi_sclk_clk.common, + &csi_mclk_clk.common, + &ve_clk.common, + &ac_dig_clk.common, + &ac_dig_4x_clk.common, + &avs_clk.common, + &mbus_clk.common, + &dsi_sclk_clk.common, + &dsi_dphy_clk.common, + &drc_clk.common, + &gpu_clk.common, + &ats_clk.common, +}; + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x", + "pll-periph", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video_2x_clk, "pll-video-2x", + "pll-video", 1, 2, 0); + +static struct clk_hw_onecell_data sun8i_a33_hw_clks = { + .hws = { + [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO] = &pll_video_clk.common.hw, + [CLK_PLL_VIDEO_2X] = &pll_video_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_CPUX] = &cpux_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_SS] = &bus_ss_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_NAND] = &bus_nand_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI] = &bus_ehci_clk.common.hw, + [CLK_BUS_OHCI] = &bus_ohci_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_LCD] = &bus_lcd_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_BUS_DE_BE] = &bus_de_be_clk.common.hw, + [CLK_BUS_DE_FE] = &bus_de_fe_clk.common.hw, + [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, + [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw, + [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, + [CLK_BUS_DRC] = &bus_drc_clk.common.hw, + [CLK_BUS_SAT] = &bus_sat_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_HSIC] = &usb_hsic_clk.common.hw, + [CLK_USB_HSIC_12M] = &usb_hsic_12M_clk.common.hw, + [CLK_USB_OHCI] = &usb_ohci_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_DRC] = &dram_drc_clk.common.hw, + [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw, + [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw, + [CLK_DE_BE] = &de_be_clk.common.hw, + [CLK_DE_FE] = &de_fe_clk.common.hw, + [CLK_LCD_CH0] = &lcd_ch0_clk.common.hw, + [CLK_LCD_CH1] = &lcd_ch1_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AC_DIG] = &ac_dig_clk.common.hw, + [CLK_AC_DIG_4X] = &ac_dig_4x_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_DSI_SCLK] = &dsi_sclk_clk.common.hw, + [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw, + [CLK_DRC] = &drc_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_ATS] = &ats_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_a33_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_HSIC] = { 0x0cc, BIT(2) }, + + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_BUS_SS] = { 0x2c0, BIT(5) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_NAND] = { 0x2c0, BIT(13) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_OTG] = { 0x2c0, BIT(24) }, + [RST_BUS_EHCI] = { 0x2c0, BIT(26) }, + [RST_BUS_OHCI] = { 0x2c0, BIT(29) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_LCD] = { 0x2c4, BIT(4) }, + [RST_BUS_CSI] = { 0x2c4, BIT(8) }, + [RST_BUS_DE_BE] = { 0x2c4, BIT(12) }, + [RST_BUS_DE_FE] = { 0x2c4, BIT(14) }, + [RST_BUS_GPU] = { 0x2c4, BIT(20) }, + [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) }, + [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) }, + [RST_BUS_DRC] = { 0x2c4, BIT(25) }, + [RST_BUS_SAT] = { 0x2c4, BIT(26) }, + + [RST_BUS_LVDS] = { 0x2c8, BIT(0) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, + [RST_BUS_UART3] = { 0x2d8, BIT(19) }, + [RST_BUS_UART4] = { 0x2d8, BIT(20) }, +}; + +static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = { + .ccu_clks = sun8i_a33_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a33_ccu_clks), + + .hw_clks = &sun8i_a33_hw_clks, + + .resets = sun8i_a33_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets), +}; + +static void __init sun8i_a33_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN8I_A33_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN8I_A33_PLL_AUDIO_REG); + + /* Force PLL-MIPI to MIPI mode */ + val = readl(reg + SUN8I_A33_PLL_MIPI_REG); + val &= ~BIT(16); + writel(val, reg + SUN8I_A33_PLL_MIPI_REG); + + sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); +} +CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu", + sun8i_a33_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 267f995..4d70590 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -184,15 +184,15 @@ static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, 0); static const char * const ahb2_parents[] = { "ahb1" , "pll-periph0" }; +static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = { + { .index = 1, .div = 2 }, +}; static struct ccu_mux ahb2_clk = { .mux = { .shift = 0, .width = 1, - - .fixed_prediv = { - .index = 1, - .div = 2, - }, + .fixed_predivs = ahb2_fixed_predivs, + .n_predivs = ARRAY_SIZE(ahb2_fixed_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 653ade5..34c3388 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -19,10 +19,29 @@ #include "ccu_common.h" #include "ccu_mux.h" +/** + * struct _ccu_div - Internal divider description + * @shift: Bit offset of the divider in its register + * @width: Width of the divider field in its register + * @max: Maximum value allowed for that divider. This is the + * arithmetic value, not the maximum value to be set in the + * register. + * @flags: clk_divider flags to apply on this divider + * @table: Divider table pointer (if applicable) + * + * That structure represents a single divider, and is meant to be + * embedded in other structures representing the various clock + * classes. + * + * It is basically a wrapper around the clk_divider functions + * arguments. + */ struct _ccu_div { u8 shift; u8 width; + u32 max; + u32 flags; struct clk_div_table *table; @@ -36,14 +55,25 @@ struct _ccu_div { .table = _table, \ } -#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \ - _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, _flags) - #define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \ _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0) +#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \ + { \ + .shift = _shift, \ + .width = _width, \ + .flags = _flags, \ + .max = _max, \ + } + +#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \ + _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags) + +#define _SUNXI_CCU_DIV_MAX(_shift, _width, _max) \ + _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0) + #define _SUNXI_CCU_DIV(_shift, _width) \ - _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, 0) + _SUNXI_CCU_DIV_FLAGS(_shift, _width, 0) struct ccu_div { u32 enable; @@ -77,13 +107,16 @@ struct ccu_div { _shift, _width, _table, 0, \ _flags) -#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ - _mshift, _mwidth, _muxshift, _muxwidth, \ - _gate, _flags) \ +#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \ + _parents, _table, \ + _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ struct ccu_div _struct = { \ .enable = _gate, \ .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ - .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \ + .mux = _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \ .common = { \ .reg = _reg, \ .hw.init = CLK_HW_INIT_PARENTS(_name, \ @@ -93,12 +126,23 @@ struct ccu_div { }, \ } +#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, _muxshift, _muxwidth, \ + _gate, _flags) \ + SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \ + _parents, NULL, \ + _reg, _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) + #define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \ _mshift, _mwidth, _muxshift, _muxwidth, \ _flags) \ - SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ - _mshift, _mwidth, _muxshift, _muxwidth, \ - 0, _flags) + SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \ + _parents, NULL, \ + _reg, _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + 0, _flags) #define SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \ diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index cbf33ef..ebb1b31 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -21,9 +21,9 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate, unsigned int best_m = 0, best_p = 0; unsigned int _m, _p; - for (_p = 0; _p <= max_p; _p++) { + for (_p = 1; _p <= max_p; _p <<= 1) { for (_m = 1; _m <= max_m; _m++) { - unsigned long tmp_rate = (parent >> _p) / _m; + unsigned long tmp_rate = parent / _p / _m; if (tmp_rate > rate) continue; @@ -46,13 +46,15 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, void *data) { struct ccu_mp *cmp = data; + unsigned int max_m, max_p; unsigned int m, p; - ccu_mp_find_best(parent_rate, rate, - 1 << cmp->m.width, (1 << cmp->p.width) - 1, - &m, &p); + max_m = cmp->m.max ?: 1 << cmp->m.width; + max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); - return (parent_rate >> p) / m; + ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); + + return parent_rate / p / m; } static void ccu_mp_disable(struct clk_hw *hw) @@ -108,13 +110,14 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, { struct ccu_mp *cmp = hw_to_ccu_mp(hw); unsigned long flags; + unsigned int max_m, max_p; unsigned int m, p; u32 reg; - ccu_mp_find_best(parent_rate, rate, - 1 << cmp->m.width, (1 << cmp->p.width) - 1, - &m, &p); + max_m = cmp->m.max ?: 1 << cmp->m.width; + max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); + ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); spin_lock_irqsave(cmp->common.lock, flags); @@ -122,7 +125,7 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); - writel(reg | (p << cmp->p.shift) | ((m - 1) << cmp->m.shift), + writel(reg | (ilog2(p) << cmp->p.shift) | ((m - 1) << cmp->m.shift), cmp->common.base + cmp->common.reg); spin_unlock_irqrestore(cmp->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h index 3cf12bf..edf9215 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.h +++ b/drivers/clk/sunxi-ng/ccu_mp.h @@ -44,7 +44,7 @@ struct ccu_mp { .enable = _gate, \ .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ - .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ .common = { \ .reg = _reg, \ .hw.init = CLK_HW_INIT_PARENTS(_name, \ diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c new file mode 100644 index 0000000..010e942 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * 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 <linux/clk-provider.h> + +#include "ccu_gate.h" +#include "ccu_mult.h" + +static void ccu_mult_find_best(unsigned long parent, unsigned long rate, + unsigned int max_n, unsigned int *n) +{ + *n = rate / parent; +} + +static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, + unsigned long parent_rate, + unsigned long rate, + void *data) +{ + struct ccu_mult *cm = data; + unsigned int n; + + ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); + + return parent_rate * n; +} + +static void ccu_mult_disable(struct clk_hw *hw) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_gate_helper_disable(&cm->common, cm->enable); +} + +static int ccu_mult_enable(struct clk_hw *hw) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_gate_helper_enable(&cm->common, cm->enable); +} + +static int ccu_mult_is_enabled(struct clk_hw *hw) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_gate_helper_is_enabled(&cm->common, cm->enable); +} + +static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + unsigned long val; + u32 reg; + + reg = readl(cm->common.base + cm->common.reg); + val = reg >> cm->mult.shift; + val &= (1 << cm->mult.width) - 1; + + ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, + &parent_rate); + + return parent_rate * (val + 1); +} + +static int ccu_mult_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_mux_helper_determine_rate(&cm->common, &cm->mux, + req, ccu_mult_round_rate, cm); +} + +static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + unsigned long flags; + unsigned int n; + u32 reg; + + ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, + &parent_rate); + + ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); + + spin_lock_irqsave(cm->common.lock, flags); + + reg = readl(cm->common.base + cm->common.reg); + reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift); + + writel(reg | ((n - 1) << cm->mult.shift), + cm->common.base + cm->common.reg); + + spin_unlock_irqrestore(cm->common.lock, flags); + + return 0; +} + +static u8 ccu_mult_get_parent(struct clk_hw *hw) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_mux_helper_get_parent(&cm->common, &cm->mux); +} + +static int ccu_mult_set_parent(struct clk_hw *hw, u8 index) +{ + struct ccu_mult *cm = hw_to_ccu_mult(hw); + + return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index); +} + +const struct clk_ops ccu_mult_ops = { + .disable = ccu_mult_disable, + .enable = ccu_mult_enable, + .is_enabled = ccu_mult_is_enabled, + + .get_parent = ccu_mult_get_parent, + .set_parent = ccu_mult_set_parent, + + .determine_rate = ccu_mult_determine_rate, + .recalc_rate = ccu_mult_recalc_rate, + .set_rate = ccu_mult_set_rate, +}; diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h index 609db66..5d2c8dc 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.h +++ b/drivers/clk/sunxi-ng/ccu_mult.h @@ -1,6 +1,9 @@ #ifndef _CCU_MULT_H_ #define _CCU_MULT_H_ +#include "ccu_common.h" +#include "ccu_mux.h" + struct _ccu_mult { u8 shift; u8 width; @@ -12,4 +15,36 @@ struct _ccu_mult { .width = _width, \ } +struct ccu_mult { + u32 enable; + + struct _ccu_mult mult; + struct ccu_mux_internal mux; + struct ccu_common common; +}; + +#define SUNXI_CCU_N_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ + _mshift, _mwidth, _gate, _lock, \ + _flags) \ + struct ccu_mult _struct = { \ + .enable = _gate, \ + .mult = _SUNXI_CCU_MULT(_mshift, _mwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &ccu_mult_ops, \ + _flags), \ + }, \ + } + +static inline struct ccu_mult *hw_to_ccu_mult(struct clk_hw *hw) +{ + struct ccu_common *common = hw_to_ccu_common(hw); + + return container_of(common, struct ccu_mult, common); +} + +extern const struct clk_ops ccu_mult_ops; + #endif /* _CCU_MULT_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 58fc36e..a43ad52 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -8,7 +8,9 @@ * the License, or (at your option) any later version. */ +#include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/delay.h> #include "ccu_gate.h" #include "ccu_mux.h" @@ -18,8 +20,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, int parent_index, unsigned long *parent_rate) { - u8 prediv = 1; + u16 prediv = 1; u32 reg; + int i; if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || (common->features & CCU_FEATURE_VARIABLE_PREDIV))) @@ -32,8 +35,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, } if (common->features & CCU_FEATURE_FIXED_PREDIV) - if (parent_index == cm->fixed_prediv.index) - prediv = cm->fixed_prediv.div; + for (i = 0; i < cm->n_predivs; i++) + if (parent_index == cm->fixed_predivs[i].index) + prediv = cm->fixed_predivs[i].div; if (common->features & CCU_FEATURE_VARIABLE_PREDIV) if (parent_index == cm->variable_prediv.index) { @@ -107,6 +111,15 @@ u8 ccu_mux_helper_get_parent(struct ccu_common *common, parent = reg >> cm->shift; parent &= (1 << cm->width) - 1; + if (cm->table) { + int num_parents = clk_hw_get_num_parents(&common->hw); + int i; + + for (i = 0; i < num_parents; i++) + if (cm->table[i] == parent) + return i; + } + return parent; } @@ -117,6 +130,9 @@ int ccu_mux_helper_set_parent(struct ccu_common *common, unsigned long flags; u32 reg; + if (cm->table) + index = cm->table[index]; + spin_lock_irqsave(common->lock, flags); reg = readl(common->base + common->reg); @@ -185,3 +201,37 @@ const struct clk_ops ccu_mux_ops = { .determine_rate = __clk_mux_determine_rate, .recalc_rate = ccu_mux_recalc_rate, }; + +/* + * This clock notifier is called when the frequency of the of the parent + * PLL clock is to be changed. The idea is to switch the parent to a + * stable clock, such as the main oscillator, while the PLL frequency + * stabilizes. + */ +static int ccu_mux_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct ccu_mux_nb *mux = to_ccu_mux_nb(nb); + int ret = 0; + + if (event == PRE_RATE_CHANGE) { + mux->original_index = ccu_mux_helper_get_parent(mux->common, + mux->cm); + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, + mux->bypass_index); + } else if (event == POST_RATE_CHANGE) { + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, + mux->original_index); + } + + udelay(mux->delay_us); + + return notifier_from_errno(ret); +} + +int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) +{ + mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb; + + return clk_notifier_register(clk, &mux_nb->clk_nb); +} diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index 9450826..47aba3a 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -5,14 +5,18 @@ #include "ccu_common.h" +struct ccu_mux_fixed_prediv { + u8 index; + u16 div; +}; + struct ccu_mux_internal { - u8 shift; - u8 width; + u8 shift; + u8 width; + const u8 *table; - struct { - u8 index; - u8 div; - } fixed_prediv; + const struct ccu_mux_fixed_prediv *fixed_predivs; + u8 n_predivs; struct { u8 index; @@ -21,12 +25,16 @@ struct ccu_mux_internal { } variable_prediv; }; -#define SUNXI_CLK_MUX(_shift, _width) \ - { \ - .shift = _shift, \ - .width = _width, \ +#define _SUNXI_CCU_MUX_TABLE(_shift, _width, _table) \ + { \ + .shift = _shift, \ + .width = _width, \ + .table = _table, \ } +#define _SUNXI_CCU_MUX(_shift, _width) \ + _SUNXI_CCU_MUX_TABLE(_shift, _width, NULL) + struct ccu_mux { u16 reg; u32 enable; @@ -35,9 +43,12 @@ struct ccu_mux { struct ccu_common common; }; -#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, _flags) \ +#define SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, _table, \ + _reg, _shift, _width, _gate, \ + _flags) \ struct ccu_mux _struct = { \ - .mux = SUNXI_CLK_MUX(_shift, _width), \ + .enable = _gate, \ + .mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \ .common = { \ .reg = _reg, \ .hw.init = CLK_HW_INIT_PARENTS(_name, \ @@ -49,17 +60,14 @@ struct ccu_mux { #define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \ _shift, _width, _gate, _flags) \ - struct ccu_mux _struct = { \ - .enable = _gate, \ - .mux = SUNXI_CLK_MUX(_shift, _width), \ - .common = { \ - .reg = _reg, \ - .hw.init = CLK_HW_INIT_PARENTS(_name, \ - _parents, \ - &ccu_mux_ops, \ - _flags), \ - } \ - } + SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \ + _reg, _shift, _width, _gate, \ + _flags) + +#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, \ + _flags) \ + SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \ + _reg, _shift, _width, 0, _flags) static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw) { @@ -88,4 +96,18 @@ int ccu_mux_helper_set_parent(struct ccu_common *common, struct ccu_mux_internal *cm, u8 index); +struct ccu_mux_nb { + struct notifier_block clk_nb; + struct ccu_common *common; + struct ccu_mux_internal *cm; + + u32 delay_us; /* How many us to wait after reparenting */ + u8 bypass_index; /* Which parent to temporarily use */ + u8 original_index; /* This is set by the notifier callback */ +}; + +#define to_ccu_mux_nb(_nb) container_of(_nb, struct ccu_mux_nb, clk_nb) + +int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb); + #endif /* _CCU_MUX_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 2071822..059fdc3 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -93,19 +93,30 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, return parent_rate * (n + 1) * (k + 1) / (m + 1); } -static long ccu_nkm_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, + unsigned long parent_rate, + unsigned long rate, + void *data) { - struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); + struct ccu_nkm *nkm = data; struct _ccu_nkm _nkm; _nkm.max_n = 1 << nkm->n.width; _nkm.max_k = 1 << nkm->k.width; - _nkm.max_m = 1 << nkm->m.width; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; - ccu_nkm_find_best(*parent_rate, rate, &_nkm); + ccu_nkm_find_best(parent_rate, rate, &_nkm); - return *parent_rate * _nkm.n * _nkm.k / _nkm.m; + return parent_rate * _nkm.n * _nkm.k / _nkm.m; +} + +static int ccu_nkm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); + + return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux, + req, ccu_nkm_round_rate, nkm); } static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, @@ -118,7 +129,7 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, _nkm.max_n = 1 << nkm->n.width; _nkm.max_k = 1 << nkm->k.width; - _nkm.max_m = 1 << nkm->m.width; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; ccu_nkm_find_best(parent_rate, rate, &_nkm); @@ -142,12 +153,29 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static u8 ccu_nkm_get_parent(struct clk_hw *hw) +{ + struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); + + return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux); +} + +static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index) +{ + struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); + + return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index); +} + const struct clk_ops ccu_nkm_ops = { .disable = ccu_nkm_disable, .enable = ccu_nkm_enable, .is_enabled = ccu_nkm_is_enabled, + .get_parent = ccu_nkm_get_parent, + .set_parent = ccu_nkm_set_parent, + + .determine_rate = ccu_nkm_determine_rate, .recalc_rate = ccu_nkm_recalc_rate, - .round_rate = ccu_nkm_round_rate, .set_rate = ccu_nkm_set_rate, }; diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h index 1936ac1..35493fd 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.h +++ b/drivers/clk/sunxi-ng/ccu_nkm.h @@ -32,10 +32,33 @@ struct ccu_nkm { struct _ccu_mult n; struct _ccu_mult k; struct _ccu_div m; + struct ccu_mux_internal mux; struct ccu_common common; }; +#define SUNXI_CCU_NKM_WITH_MUX_GATE_LOCK(_struct, _name, _parents, _reg, \ + _nshift, _nwidth, \ + _kshift, _kwidth, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _lock, _flags) \ + struct ccu_nkm _struct = { \ + .enable = _gate, \ + .lock = _lock, \ + .k = _SUNXI_CCU_MULT(_kshift, _kwidth), \ + .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ + .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parents, \ + &ccu_nkm_ops, \ + _flags), \ + }, \ + } + #define SUNXI_CCU_NKM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ _nshift, _nwidth, \ _kshift, _kwidth, \ diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index 9f2b98e..9769dee 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -29,14 +29,14 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, unsigned long _n, _k, _m, _p; for (_k = 1; _k <= nkmp->max_k; _k++) { - for (_p = 0; _p <= nkmp->max_p; _p++) { + for (_p = 1; _p <= nkmp->max_p; _p <<= 1) { unsigned long tmp_rate; - rational_best_approximation(rate / _k, parent >> _p, + rational_best_approximation(rate / _k, parent / _p, nkmp->max_n, nkmp->max_m, &_n, &_m); - tmp_rate = (parent * _n * _k >> _p) / _m; + tmp_rate = parent * _n * _k / (_m * _p); if (tmp_rate > rate) continue; @@ -110,13 +110,12 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, _nkmp.max_n = 1 << nkmp->n.width; _nkmp.max_k = 1 << nkmp->k.width; - _nkmp.max_m = 1 << nkmp->m.width; - _nkmp.max_p = (1 << nkmp->p.width) - 1; + _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; + _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); - ccu_nkmp_find_best(*parent_rate, rate, - &_nkmp); + ccu_nkmp_find_best(*parent_rate, rate, &_nkmp); - return (*parent_rate * _nkmp.n * _nkmp.k >> _nkmp.p) / _nkmp.m; + return *parent_rate * _nkmp.n * _nkmp.k / (_nkmp.m * _nkmp.p); } static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, @@ -129,8 +128,8 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, _nkmp.max_n = 1 << nkmp->n.width; _nkmp.max_k = 1 << nkmp->k.width; - _nkmp.max_m = 1 << nkmp->m.width; - _nkmp.max_p = (1 << nkmp->p.width) - 1; + _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; + _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); ccu_nkmp_find_best(parent_rate, rate, &_nkmp); @@ -145,7 +144,7 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, reg |= (_nkmp.n - 1) << nkmp->n.shift; reg |= (_nkmp.k - 1) << nkmp->k.shift; reg |= (_nkmp.m - 1) << nkmp->m.shift; - reg |= _nkmp.p << nkmp->p.shift; + reg |= ilog2(_nkmp.p) << nkmp->p.shift; writel(reg, nkmp->common.base + nkmp->common.reg); diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index e35ddd8..b61bdd8 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -61,11 +61,13 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct ccu_nm *nm = hw_to_ccu_nm(hw); + unsigned long max_n, max_m; unsigned long n, m; - rational_best_approximation(rate, *parent_rate, - 1 << nm->n.width, 1 << nm->m.width, - &n, &m); + max_n = 1 << nm->n.width; + max_m = nm->m.max ?: 1 << nm->m.width; + + rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m); return *parent_rate * n / m; } @@ -75,6 +77,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, { struct ccu_nm *nm = hw_to_ccu_nm(hw); unsigned long flags; + unsigned long max_n, max_m; unsigned long n, m; u32 reg; @@ -83,9 +86,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, else ccu_frac_helper_disable(&nm->common, &nm->frac); - rational_best_approximation(rate, parent_rate, - 1 << nm->n.width, 1 << nm->m.width, - &n, &m); + max_n = 1 << nm->n.width; + max_m = nm->m.max ?: 1 << nm->m.width; + + rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m); spin_lock_irqsave(nm->common.lock, flags); diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index b38d71c..e54266c 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -91,7 +91,8 @@ static void __init sun4i_a10_mod0_setup(struct device_node *node) sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock, reg); } -CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); +CLK_OF_DECLARE_DRIVER(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", + sun4i_a10_mod0_setup); static int sun4i_a10_mod0_clk_probe(struct platform_device *pdev) { diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c index a5666e1..ea1eed2 100644 --- a/drivers/clk/sunxi/clk-sun8i-apb0.c +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -82,8 +82,8 @@ err_unmap: of_address_to_resource(node, 0, &res); release_mem_region(res.start, resource_size(&res)); } -CLK_OF_DECLARE(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", - sun8i_a23_apb0_setup); +CLK_OF_DECLARE_DRIVER(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", + sun8i_a23_apb0_setup); static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) { diff --git a/drivers/clk/uniphier/Kconfig b/drivers/clk/uniphier/Kconfig new file mode 100644 index 0000000..5512377 --- /dev/null +++ b/drivers/clk/uniphier/Kconfig @@ -0,0 +1,9 @@ +config CLK_UNIPHIER + bool "Clock driver for UniPhier SoCs" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on OF && MFD_SYSCON + default ARCH_UNIPHIER + help + Support for clock controllers on UniPhier SoCs. + Say Y if you want to control clocks provided by System Control + block, Media I/O block, Peripheral Block. diff --git a/drivers/clk/uniphier/Makefile b/drivers/clk/uniphier/Makefile new file mode 100644 index 0000000..f27b3603 --- /dev/null +++ b/drivers/clk/uniphier/Makefile @@ -0,0 +1,8 @@ +obj-y += clk-uniphier-core.o +obj-y += clk-uniphier-fixed-factor.o +obj-y += clk-uniphier-fixed-rate.o +obj-y += clk-uniphier-gate.o +obj-y += clk-uniphier-mux.o +obj-y += clk-uniphier-sys.o +obj-y += clk-uniphier-mio.o +obj-y += clk-uniphier-peri.o diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c new file mode 100644 index 0000000..5ffb898 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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/init.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include "clk-uniphier.h" + +static struct clk_hw *uniphier_clk_register(struct device *dev, + struct regmap *regmap, + const struct uniphier_clk_data *data) +{ + switch (data->type) { + case UNIPHIER_CLK_TYPE_FIXED_FACTOR: + return uniphier_clk_register_fixed_factor(dev, data->name, + &data->data.factor); + case UNIPHIER_CLK_TYPE_FIXED_RATE: + return uniphier_clk_register_fixed_rate(dev, data->name, + &data->data.rate); + case UNIPHIER_CLK_TYPE_GATE: + return uniphier_clk_register_gate(dev, regmap, data->name, + &data->data.gate); + case UNIPHIER_CLK_TYPE_MUX: + return uniphier_clk_register_mux(dev, regmap, data->name, + &data->data.mux); + default: + dev_err(dev, "unsupported clock type\n"); + return ERR_PTR(-EINVAL); + } +} + +static int uniphier_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *hw_data; + const struct uniphier_clk_data *p, *data; + struct regmap *regmap; + struct device_node *parent; + int clk_num = 0; + + data = of_device_get_match_data(dev); + if (WARN_ON(!data)) + return -EINVAL; + + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get regmap (error %ld)\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (p = data; p->name; p++) + clk_num = max(clk_num, p->idx + 1); + + hw_data = devm_kzalloc(dev, + sizeof(*hw_data) + clk_num * sizeof(struct clk_hw *), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + hw_data->num = clk_num; + + /* avoid returning NULL for unused idx */ + for (; clk_num >= 0; clk_num--) + hw_data->hws[clk_num] = ERR_PTR(-EINVAL); + + for (p = data; p->name; p++) { + struct clk_hw *hw; + + dev_dbg(dev, "register %s (index=%d)\n", p->name, p->idx); + hw = uniphier_clk_register(dev, regmap, p); + if (IS_ERR(hw)) { + dev_err(dev, "failed to register %s (error %ld)\n", + p->name, PTR_ERR(hw)); + return PTR_ERR(hw); + } + + if (p->idx >= 0) + hw_data->hws[p->idx] = hw; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + hw_data); +} + +static int uniphier_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id uniphier_clk_match[] = { + /* System clock */ + { + .compatible = "socionext,uniphier-ld4-clock", + .data = uniphier_ld4_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pro4-clock", + .data = uniphier_pro4_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-sld8-clock", + .data = uniphier_sld8_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pro5-clock", + .data = uniphier_pro5_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs2-clock", + .data = uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-ld11-clock", + .data = uniphier_ld11_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-ld20-clock", + .data = uniphier_ld20_sys_clk_data, + }, + /* Media I/O clock */ + { + .compatible = "socionext,uniphier-sld3-mio-clock", + .data = uniphier_sld3_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-ld4-mio-clock", + .data = uniphier_sld3_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pro4-mio-clock", + .data = uniphier_sld3_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-sld8-mio-clock", + .data = uniphier_sld3_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pro5-mio-clock", + .data = uniphier_pro5_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs2-mio-clock", + .data = uniphier_pro5_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-ld11-mio-clock", + .data = uniphier_sld3_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-ld20-mio-clock", + .data = uniphier_pro5_mio_clk_data, + }, + /* Peripheral clock */ + { + .compatible = "socionext,uniphier-ld4-peri-clock", + .data = uniphier_ld4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-pro4-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-sld8-peri-clock", + .data = uniphier_ld4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-pro5-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs2-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-ld11-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + { + .compatible = "socionext,uniphier-ld20-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + { /* sentinel */ } +}; + +static struct platform_driver uniphier_clk_driver = { + .probe = uniphier_clk_probe, + .remove = uniphier_clk_remove, + .driver = { + .name = "uniphier-clk", + .of_match_table = uniphier_clk_match, + }, +}; +builtin_platform_driver(uniphier_clk_driver); diff --git a/drivers/clk/uniphier/clk-uniphier-fixed-factor.c b/drivers/clk/uniphier/clk-uniphier-fixed-factor.c new file mode 100644 index 0000000..da2d9f4 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-fixed-factor.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 "clk-uniphier.h" + +struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev, + const char *name, + const struct uniphier_clk_fixed_factor_data *data) +{ + struct clk_fixed_factor *fix; + struct clk_init_data init; + int ret; + + fix = devm_kzalloc(dev, sizeof(*fix), GFP_KERNEL); + if (!fix) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixed_factor_ops; + init.flags = data->parent_name ? CLK_SET_RATE_PARENT : 0; + init.parent_names = data->parent_name ? &data->parent_name : NULL; + init.num_parents = data->parent_name ? 1 : 0; + + fix->mult = data->mult; + fix->div = data->div; + fix->hw.init = &init; + + ret = devm_clk_hw_register(dev, &fix->hw); + if (ret) + return ERR_PTR(ret); + + return &fix->hw; +} diff --git a/drivers/clk/uniphier/clk-uniphier-fixed-rate.c b/drivers/clk/uniphier/clk-uniphier-fixed-rate.c new file mode 100644 index 0000000..0ad0d46 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-fixed-rate.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 "clk-uniphier.h" + +struct clk_hw *uniphier_clk_register_fixed_rate(struct device *dev, + const char *name, + const struct uniphier_clk_fixed_rate_data *data) +{ + struct clk_fixed_rate *fixed; + struct clk_init_data init; + int ret; + + /* allocate fixed-rate clock */ + fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixed_rate_ops; + init.parent_names = NULL; + init.num_parents = 0; + + fixed->fixed_rate = data->fixed_rate; + fixed->hw.init = &init; + + ret = devm_clk_hw_register(dev, &fixed->hw); + if (ret) + return ERR_PTR(ret); + + return &fixed->hw; +} diff --git a/drivers/clk/uniphier/clk-uniphier-gate.c b/drivers/clk/uniphier/clk-uniphier-gate.c new file mode 100644 index 0000000..49142d4 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-gate.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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/regmap.h> + +#include "clk-uniphier.h" + +struct uniphier_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; +}; + +#define to_uniphier_clk_gate(_hw) \ + container_of(_hw, struct uniphier_clk_gate, hw) + +static int uniphier_clk_gate_endisable(struct clk_hw *hw, int enable) +{ + struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw); + + return regmap_write_bits(gate->regmap, gate->reg, BIT(gate->bit), + enable ? BIT(gate->bit) : 0); +} + +static int uniphier_clk_gate_enable(struct clk_hw *hw) +{ + return uniphier_clk_gate_endisable(hw, 1); +} + +static void uniphier_clk_gate_disable(struct clk_hw *hw) +{ + if (uniphier_clk_gate_endisable(hw, 0) < 0) + pr_warn("failed to disable clk\n"); +} + +static int uniphier_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw); + unsigned int val; + + if (regmap_read(gate->regmap, gate->reg, &val) < 0) + pr_warn("is_enabled() may return wrong result\n"); + + return !!(val & BIT(gate->bit)); +} + +static const struct clk_ops uniphier_clk_gate_ops = { + .enable = uniphier_clk_gate_enable, + .disable = uniphier_clk_gate_disable, + .is_enabled = uniphier_clk_gate_is_enabled, +}; + +struct clk_hw *uniphier_clk_register_gate(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_gate_data *data) +{ + struct uniphier_clk_gate *gate; + struct clk_init_data init; + int ret; + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &uniphier_clk_gate_ops; + init.flags = data->parent_name ? CLK_SET_RATE_PARENT : 0; + init.parent_names = data->parent_name ? &data->parent_name : NULL; + init.num_parents = data->parent_name ? 1 : 0; + + gate->regmap = regmap; + gate->reg = data->reg; + gate->bit = data->bit; + gate->hw.init = &init; + + ret = devm_clk_hw_register(dev, &gate->hw); + if (ret) + return ERR_PTR(ret); + + return &gate->hw; +} diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c new file mode 100644 index 0000000..6aa7ec7 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-mio.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 "clk-uniphier.h" + +#define UNIPHIER_MIO_CLK_SD_FIXED \ + UNIPHIER_CLK_FACTOR("sd-44m", -1, "sd-133m", 1, 3), \ + UNIPHIER_CLK_FACTOR("sd-33m", -1, "sd-200m", 1, 6), \ + UNIPHIER_CLK_FACTOR("sd-50m", -1, "sd-200m", 1, 4), \ + UNIPHIER_CLK_FACTOR("sd-67m", -1, "sd-200m", 1, 3), \ + UNIPHIER_CLK_FACTOR("sd-100m", -1, "sd-200m", 1, 2), \ + UNIPHIER_CLK_FACTOR("sd-40m", -1, "sd-200m", 1, 5), \ + UNIPHIER_CLK_FACTOR("sd-25m", -1, "sd-200m", 1, 8), \ + UNIPHIER_CLK_FACTOR("sd-22m", -1, "sd-133m", 1, 6) + +#define UNIPHIER_MIO_CLK_SD(_idx, ch) \ + { \ + .name = "sd" #ch "-sel", \ + .type = UNIPHIER_CLK_TYPE_MUX, \ + .idx = -1, \ + .data.mux = { \ + .parent_names = { \ + "sd-44m", \ + "sd-33m", \ + "sd-50m", \ + "sd-67m", \ + "sd-100m", \ + "sd-40m", \ + "sd-25m", \ + "sd-22m", \ + }, \ + .num_parents = 8, \ + .reg = 0x30 + 0x200 * (ch), \ + .masks = { \ + 0x00031000, \ + 0x00031000, \ + 0x00031000, \ + 0x00031000, \ + 0x00001300, \ + 0x00001300, \ + 0x00001300, \ + 0x00001300, \ + }, \ + .vals = { \ + 0x00000000, \ + 0x00010000, \ + 0x00020000, \ + 0x00030000, \ + 0x00001000, \ + 0x00001100, \ + 0x00001200, \ + 0x00001300, \ + }, \ + }, \ + }, \ + UNIPHIER_CLK_GATE("sd" #ch, (_idx), "sd" #ch "-sel", 0x20 + 0x200 * (ch), 8) + +#define UNIPHIER_MIO_CLK_USB2(idx, ch) \ + UNIPHIER_CLK_GATE("usb2" #ch, (idx), "usb2", 0x20 + 0x200 * (ch), 28) + +#define UNIPHIER_MIO_CLK_USB2_PHY(idx, ch) \ + UNIPHIER_CLK_GATE("usb2" #ch "-phy", (idx), "usb2", 0x20 + 0x200 * (ch), 29) + +#define UNIPHIER_MIO_CLK_DMAC(idx) \ + UNIPHIER_CLK_GATE("miodmac", (idx), "stdmac", 0x20, 25) + +const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = { + UNIPHIER_MIO_CLK_SD_FIXED, + UNIPHIER_MIO_CLK_SD(0, 0), + UNIPHIER_MIO_CLK_SD(1, 1), + UNIPHIER_MIO_CLK_SD(2, 2), + UNIPHIER_MIO_CLK_DMAC(7), + UNIPHIER_MIO_CLK_USB2(8, 0), + UNIPHIER_MIO_CLK_USB2(9, 1), + UNIPHIER_MIO_CLK_USB2(10, 2), + UNIPHIER_MIO_CLK_USB2(11, 3), + UNIPHIER_MIO_CLK_USB2_PHY(12, 0), + UNIPHIER_MIO_CLK_USB2_PHY(13, 1), + UNIPHIER_MIO_CLK_USB2_PHY(14, 2), + UNIPHIER_MIO_CLK_USB2_PHY(15, 3), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pro5_mio_clk_data[] = { + UNIPHIER_MIO_CLK_SD_FIXED, + UNIPHIER_MIO_CLK_SD(0, 0), + UNIPHIER_MIO_CLK_SD(1, 1), + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier-mux.c b/drivers/clk/uniphier/clk-uniphier-mux.c new file mode 100644 index 0000000..15a2f2c --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-mux.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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/regmap.h> + +#include "clk-uniphier.h" + +struct uniphier_clk_mux { + struct clk_hw hw; + struct regmap *regmap; + unsigned int reg; + const unsigned int *masks; + const unsigned int *vals; +}; + +#define to_uniphier_clk_mux(_hw) container_of(_hw, struct uniphier_clk_mux, hw) + +static int uniphier_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw); + + return regmap_write_bits(mux->regmap, mux->reg, mux->masks[index], + mux->vals[index]); +} + +static u8 uniphier_clk_mux_get_parent(struct clk_hw *hw) +{ + struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw); + int num_parents = clk_hw_get_num_parents(hw); + int ret; + u32 val; + u8 i; + + ret = regmap_read(mux->regmap, mux->reg, &val); + if (ret) + return ret; + + for (i = 0; i < num_parents; i++) + if ((mux->masks[i] & val) == mux->vals[i]) + return i; + + return -EINVAL; +} + +static const struct clk_ops uniphier_clk_mux_ops = { + .determine_rate = __clk_mux_determine_rate, + .set_parent = uniphier_clk_mux_set_parent, + .get_parent = uniphier_clk_mux_get_parent, +}; + +struct clk_hw *uniphier_clk_register_mux(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_mux_data *data) +{ + struct uniphier_clk_mux *mux; + struct clk_init_data init; + int ret; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &uniphier_clk_mux_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = data->parent_names; + init.num_parents = data->num_parents, + + mux->regmap = regmap; + mux->reg = data->reg; + mux->masks = data->masks; + mux->vals = data->vals; + mux->hw.init = &init; + + ret = devm_clk_hw_register(dev, &mux->hw); + if (ret) + return ERR_PTR(ret); + + return &mux->hw; +} diff --git a/drivers/clk/uniphier/clk-uniphier-peri.c b/drivers/clk/uniphier/clk-uniphier-peri.c new file mode 100644 index 0000000..521c80e --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-peri.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 "clk-uniphier.h" + +#define UNIPHIER_PERI_CLK_UART(idx, ch) \ + UNIPHIER_CLK_GATE("uart" #ch, (idx), "uart", 0x24, 19 + (ch)) + +#define UNIPHIER_PERI_CLK_I2C_COMMON \ + UNIPHIER_CLK_GATE("i2c-common", -1, "i2c", 0x20, 1) + +#define UNIPHIER_PERI_CLK_I2C(idx, ch) \ + UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c-common", 0x24, 5 + (ch)) + +#define UNIPHIER_PERI_CLK_FI2C(idx, ch) \ + UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c", 0x24, 24 + (ch)) + +const struct uniphier_clk_data uniphier_ld4_peri_clk_data[] = { + UNIPHIER_PERI_CLK_UART(0, 0), + UNIPHIER_PERI_CLK_UART(1, 1), + UNIPHIER_PERI_CLK_UART(2, 2), + UNIPHIER_PERI_CLK_UART(3, 3), + UNIPHIER_PERI_CLK_I2C_COMMON, + UNIPHIER_PERI_CLK_I2C(4, 0), + UNIPHIER_PERI_CLK_I2C(5, 1), + UNIPHIER_PERI_CLK_I2C(6, 2), + UNIPHIER_PERI_CLK_I2C(7, 3), + UNIPHIER_PERI_CLK_I2C(8, 4), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pro4_peri_clk_data[] = { + UNIPHIER_PERI_CLK_UART(0, 0), + UNIPHIER_PERI_CLK_UART(1, 1), + UNIPHIER_PERI_CLK_UART(2, 2), + UNIPHIER_PERI_CLK_UART(3, 3), + UNIPHIER_PERI_CLK_FI2C(4, 0), + UNIPHIER_PERI_CLK_FI2C(5, 1), + UNIPHIER_PERI_CLK_FI2C(6, 2), + UNIPHIER_PERI_CLK_FI2C(7, 3), + UNIPHIER_PERI_CLK_FI2C(8, 4), + UNIPHIER_PERI_CLK_FI2C(9, 5), + UNIPHIER_PERI_CLK_FI2C(10, 6), + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c new file mode 100644 index 0000000..5d02999 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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/stddef.h> + +#include "clk-uniphier.h" + +#define UNIPHIER_SLD3_SYS_CLK_SD \ + UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 8), \ + UNIPHIER_CLK_FACTOR("sd-133m", -1, "vpll27a", 1, 2) + +#define UNIPHIER_PRO5_SYS_CLK_SD \ + UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 12), \ + UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 18) + +#define UNIPHIER_LD20_SYS_CLK_SD \ + UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \ + UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) + +#define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \ + UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10) + +#define UNIPHIER_LD11_SYS_CLK_STDMAC(idx) \ + UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x210c, 8) + +#define UNIPHIER_PRO4_SYS_CLK_GIO(idx) \ + UNIPHIER_CLK_GATE("gio", (idx), NULL, 0x2104, 6) + +#define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \ + UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch)) + +const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ + UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */ + UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */ + UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ + UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */ + UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */ + UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 64, 1), /* 1600 MHz */ + UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */ + UNIPHIER_CLK_FACTOR("a2pll", -1, "upll", 256, 125), /* 589.824 MHz */ + UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), + UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ + UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */ + UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), + UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 64, 1), /* 1600 MHz */ + UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */ + UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 120, 1), /* 2400 MHz */ + UNIPHIER_CLK_FACTOR("dapll1", -1, "ref", 128, 1), /* 2560 MHz */ + UNIPHIER_CLK_FACTOR("dapll2", -1, "ref", 144, 125), /* 2949.12 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_PRO5_SYS_CLK_SD, + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */ + UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ + UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), + UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 96, 1), /* 2400 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 27), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_PRO5_SYS_CLK_SD, + UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */ + /* GIO is always clock-enabled: no function for 0x2104 bit6 */ + UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), + UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + /* The document mentions 0x2104 bit 18, but not functional */ + UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19), + UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */ + UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD20_SYS_CLK_SD, + UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */ + /* GIO is always clock-enabled: no function for 0x210c bit5 */ + /* + * clock for USB Link is enabled by the logic "OR" of bit 14 and bit 15. + * We do not use bit 15 here. + */ + UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), + UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), + UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h new file mode 100644 index 0000000..3ae1840 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 __CLK_UNIPHIER_H__ +#define __CLK_UNIPHIER_H__ + +struct clk_hw; +struct device; +struct regmap; + +#define UNIPHIER_CLK_MUX_MAX_PARENTS 8 + +enum uniphier_clk_type { + UNIPHIER_CLK_TYPE_FIXED_FACTOR, + UNIPHIER_CLK_TYPE_FIXED_RATE, + UNIPHIER_CLK_TYPE_GATE, + UNIPHIER_CLK_TYPE_MUX, +}; + +struct uniphier_clk_fixed_factor_data { + const char *parent_name; + unsigned int mult; + unsigned int div; +}; + +struct uniphier_clk_fixed_rate_data { + unsigned long fixed_rate; +}; + +struct uniphier_clk_gate_data { + const char *parent_name; + unsigned int reg; + unsigned int bit; +}; + +struct uniphier_clk_mux_data { + const char *parent_names[UNIPHIER_CLK_MUX_MAX_PARENTS]; + unsigned int num_parents; + unsigned int reg; + unsigned int masks[UNIPHIER_CLK_MUX_MAX_PARENTS]; + unsigned int vals[UNIPHIER_CLK_MUX_MAX_PARENTS]; +}; + +struct uniphier_clk_data { + const char *name; + enum uniphier_clk_type type; + int idx; + union { + struct uniphier_clk_fixed_factor_data factor; + struct uniphier_clk_fixed_rate_data rate; + struct uniphier_clk_gate_data gate; + struct uniphier_clk_mux_data mux; + } data; +}; + +#define UNIPHIER_CLK_FACTOR(_name, _idx, _parent, _mult, _div) \ + { \ + .name = (_name), \ + .type = UNIPHIER_CLK_TYPE_FIXED_FACTOR, \ + .idx = (_idx), \ + .data.factor = { \ + .parent_name = (_parent), \ + .mult = (_mult), \ + .div = (_div), \ + }, \ + } + + +#define UNIPHIER_CLK_GATE(_name, _idx, _parent, _reg, _bit) \ + { \ + .name = (_name), \ + .type = UNIPHIER_CLK_TYPE_GATE, \ + .idx = (_idx), \ + .data.gate = { \ + .parent_name = (_parent), \ + .reg = (_reg), \ + .bit = (_bit), \ + }, \ + } + + +struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev, + const char *name, + const struct uniphier_clk_fixed_factor_data *data); +struct clk_hw *uniphier_clk_register_fixed_rate(struct device *dev, + const char *name, + const struct uniphier_clk_fixed_rate_data *data); +struct clk_hw *uniphier_clk_register_gate(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_gate_data *data); +struct clk_hw *uniphier_clk_register_mux(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_mux_data *data); + +extern const struct uniphier_clk_data uniphier_sld3_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld4_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_pro4_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_sld8_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_pro5_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld11_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_sld3_mio_clk_data[]; +extern const struct uniphier_clk_data uniphier_pro5_mio_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld4_peri_clk_data[]; +extern const struct uniphier_clk_data uniphier_pro4_peri_clk_data[]; + +#endif /* __CLK_UNIPHIER_H__ */ diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c index 5e9b652..4faa944 100644 --- a/drivers/clk/versatile/clk-icst.c +++ b/drivers/clk/versatile/clk-icst.c @@ -27,6 +27,26 @@ /* Magic unlocking token used on all Versatile boards */ #define VERSATILE_LOCK_VAL 0xA05F +#define VERSATILE_AUX_OSC_BITS 0x7FFFF +#define INTEGRATOR_AP_CM_BITS 0xFF +#define INTEGRATOR_AP_SYS_BITS 0xFF +#define INTEGRATOR_CP_CM_CORE_BITS 0x7FF +#define INTEGRATOR_CP_CM_MEM_BITS 0x7FF000 + +#define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8) + +/** + * enum icst_control_type - the type of ICST control register + */ +enum icst_control_type { + ICST_VERSATILE, /* The standard type, all control bits available */ + ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */ + ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */ + ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */ + ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */ + ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */ +}; + /** * struct clk_icst - ICST VCO clock wrapper * @hw: corresponding clock hardware entry @@ -34,6 +54,7 @@ * @lockreg: VCO lock register address * @params: parameters for this ICST instance * @rate: current rate + * @ctype: the type of control register for the ICST */ struct clk_icst { struct clk_hw hw; @@ -42,6 +63,7 @@ struct clk_icst { u32 lockreg_off; struct icst_params *params; unsigned long rate; + enum icst_control_type ctype; }; #define to_icst(_hw) container_of(_hw, struct clk_icst, hw) @@ -59,6 +81,76 @@ static int vco_get(struct clk_icst *icst, struct icst_vco *vco) ret = regmap_read(icst->map, icst->vcoreg_off, &val); if (ret) return ret; + + /* + * The Integrator/AP core clock can only access the low eight + * bits of the v PLL divider. Bit 8 is tied low and always zero, + * r is hardwired to 22 and output divider s is hardwired to 1 + * (divide by 2) according to the document + * "Integrator CM926EJ-S, CM946E-S, CM966E-S, CM1026EJ-S and + * CM1136JF-S User Guide" ARM DUI 0138E, page 3-13 thru 3-14. + */ + if (icst->ctype == ICST_INTEGRATOR_AP_CM) { + vco->v = val & INTEGRATOR_AP_CM_BITS; + vco->r = 22; + vco->s = 1; + return 0; + } + + /* + * The Integrator/AP system clock on the base board can only + * access the low eight bits of the v PLL divider. Bit 8 is tied low + * and always zero, r is hardwired to 46, and the output divider is + * hardwired to 3 (divide by 4) according to the document + * "Integrator AP ASIC Development Motherboard" ARM DUI 0098B, + * page 3-16. + */ + if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { + vco->v = val & INTEGRATOR_AP_SYS_BITS; + vco->r = 46; + vco->s = 3; + return 0; + } + + /* + * The Integrator/AP PCI clock is using an odd pattern to create + * the child clock, basically a single bit called DIVX/Y is used + * to select between two different hardwired values: setting the + * bit to 0 yields v = 17, r = 22 and OD = 1, whereas setting the + * bit to 1 yields v = 14, r = 14 and OD = 1 giving the frequencies + * 33 or 25 MHz respectively. + */ + if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { + bool divxy = !!(val & INTEGRATOR_AP_PCI_25_33_MHZ); + + vco->v = divxy ? 17 : 14; + vco->r = divxy ? 22 : 14; + vco->s = 1; + return 0; + } + + /* + * The Integrator/CP core clock can access the low eight bits + * of the v PLL divider. Bit 8 is tied low and always zero, + * r is hardwired to 22 and the output divider s is accessible + * in bits 8 thru 10 according to the document + * "Integrator/CM940T, CM920T, CM740T, and CM720T User Guide" + * ARM DUI 0157A, page 3-20 thru 3-23 and 4-10. + */ + if (icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { + vco->v = val & 0xFF; + vco->r = 22; + vco->s = (val >> 8) & 7; + return 0; + } + + if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { + vco->v = (val >> 12) & 0xFF; + vco->r = 22; + vco->s = (val >> 20) & 7; + return 0; + } + vco->v = val & 0x1ff; vco->r = (val >> 9) & 0x7f; vco->s = (val >> 16) & 03; @@ -72,22 +164,62 @@ static int vco_get(struct clk_icst *icst, struct icst_vco *vco) */ static int vco_set(struct clk_icst *icst, struct icst_vco vco) { + u32 mask; u32 val; int ret; - ret = regmap_read(icst->map, icst->vcoreg_off, &val); - if (ret) - return ret; + /* Mask the bits used by the VCO */ + switch (icst->ctype) { + case ICST_INTEGRATOR_AP_CM: + mask = INTEGRATOR_AP_CM_BITS; + val = vco.v & 0xFF; + if (vco.v & 0x100) + pr_err("ICST error: tried to set bit 8 of VDW\n"); + if (vco.s != 1) + pr_err("ICST error: tried to use VOD != 1\n"); + if (vco.r != 22) + pr_err("ICST error: tried to use RDW != 22\n"); + break; + case ICST_INTEGRATOR_AP_SYS: + mask = INTEGRATOR_AP_SYS_BITS; + val = vco.v & 0xFF; + if (vco.v & 0x100) + pr_err("ICST error: tried to set bit 8 of VDW\n"); + if (vco.s != 3) + pr_err("ICST error: tried to use VOD != 1\n"); + if (vco.r != 46) + pr_err("ICST error: tried to use RDW != 22\n"); + break; + case ICST_INTEGRATOR_CP_CM_CORE: + mask = INTEGRATOR_CP_CM_CORE_BITS; /* Uses 12 bits */ + val = (vco.v & 0xFF) | vco.s << 8; + if (vco.v & 0x100) + pr_err("ICST error: tried to set bit 8 of VDW\n"); + if (vco.r != 22) + pr_err("ICST error: tried to use RDW != 22\n"); + break; + case ICST_INTEGRATOR_CP_CM_MEM: + mask = INTEGRATOR_CP_CM_MEM_BITS; /* Uses 12 bits */ + val = ((vco.v & 0xFF) << 12) | (vco.s << 20); + if (vco.v & 0x100) + pr_err("ICST error: tried to set bit 8 of VDW\n"); + if (vco.r != 22) + pr_err("ICST error: tried to use RDW != 22\n"); + break; + default: + /* Regular auxilary oscillator */ + mask = VERSATILE_AUX_OSC_BITS; + val = vco.v | (vco.r << 9) | (vco.s << 16); + break; + } - /* Mask the 18 bits used by the VCO */ - val &= ~0x7ffff; - val |= vco.v | (vco.r << 9) | (vco.s << 16); + pr_debug("ICST: new val = 0x%08x\n", val); /* This magic unlocks the VCO so it can be controlled */ ret = regmap_write(icst->map, icst->lockreg_off, VERSATILE_LOCK_VAL); if (ret) return ret; - ret = regmap_write(icst->map, icst->vcoreg_off, val); + ret = regmap_update_bits(icst->map, icst->vcoreg_off, mask, val); if (ret) return ret; /* This locks the VCO again */ @@ -121,6 +253,46 @@ static long icst_round_rate(struct clk_hw *hw, unsigned long rate, struct clk_icst *icst = to_icst(hw); struct icst_vco vco; + if (icst->ctype == ICST_INTEGRATOR_AP_CM || + icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { + if (rate <= 12000000) + return 12000000; + if (rate >= 160000000) + return 160000000; + /* Slam to closest megahertz */ + return DIV_ROUND_CLOSEST(rate, 1000000) * 1000000; + } + + if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { + if (rate <= 6000000) + return 6000000; + if (rate >= 66000000) + return 66000000; + /* Slam to closest 0.5 megahertz */ + return DIV_ROUND_CLOSEST(rate, 500000) * 500000; + } + + if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { + /* Divides between 3 and 50 MHz in steps of 0.25 MHz */ + if (rate <= 3000000) + return 3000000; + if (rate >= 50000000) + return 5000000; + /* Slam to closest 0.25 MHz */ + return DIV_ROUND_CLOSEST(rate, 250000) * 250000; + } + + if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { + /* + * If we're below or less than halfway from 25 to 33 MHz + * select 25 MHz + */ + if (rate <= 25000000 || rate < 29000000) + return 25000000; + /* Else just return the default frequency */ + return 33000000; + } + vco = icst_hz_to_vco(icst->params, rate); return icst_hz(icst->params, vco); } @@ -131,6 +303,36 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_icst *icst = to_icst(hw); struct icst_vco vco; + if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { + /* This clock is especially primitive */ + unsigned int val; + int ret; + + if (rate == 25000000) { + val = 0; + } else if (rate == 33000000) { + val = INTEGRATOR_AP_PCI_25_33_MHZ; + } else { + pr_err("ICST: cannot set PCI frequency %lu\n", + rate); + return -EINVAL; + } + ret = regmap_write(icst->map, icst->lockreg_off, + VERSATILE_LOCK_VAL); + if (ret) + return ret; + ret = regmap_update_bits(icst->map, icst->vcoreg_off, + INTEGRATOR_AP_PCI_25_33_MHZ, + val); + if (ret) + return ret; + /* This locks the VCO again */ + ret = regmap_write(icst->map, icst->lockreg_off, 0); + if (ret) + return ret; + return 0; + } + if (parent_rate) icst->params->ref = parent_rate; vco = icst_hz_to_vco(icst->params, rate); @@ -148,7 +350,8 @@ static struct clk *icst_clk_setup(struct device *dev, const struct clk_icst_desc *desc, const char *name, const char *parent_name, - struct regmap *map) + struct regmap *map, + enum icst_control_type ctype) { struct clk *clk; struct clk_icst *icst; @@ -178,6 +381,7 @@ static struct clk *icst_clk_setup(struct device *dev, icst->params = pclone; icst->vcoreg_off = desc->vco_offset; icst->lockreg_off = desc->lock_offset; + icst->ctype = ctype; clk = clk_register(dev, &icst->hw); if (IS_ERR(clk)) { @@ -206,7 +410,8 @@ struct clk *icst_clk_register(struct device *dev, pr_err("could not initialize ICST regmap\n"); return ERR_CAST(map); } - return icst_clk_setup(dev, desc, name, parent_name, map); + return icst_clk_setup(dev, desc, name, parent_name, map, + ICST_VERSATILE); } EXPORT_SYMBOL_GPL(icst_clk_register); @@ -239,6 +444,56 @@ static const struct icst_params icst307_params = { .idx2s = icst307_idx2s, }; +/** + * The core modules on the Integrator/AP and Integrator/CP have + * especially crippled ICST525 control. + */ +static const struct icst_params icst525_apcp_cm_params = { + .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, + /* Minimum 12 MHz, VDW = 4 */ + .vd_min = 12, + /* + * Maximum 160 MHz, VDW = 152 for all core modules, but + * CM926EJ-S, CM1026EJ-S and CM1136JF-S can actually + * go to 200 MHz (max VDW = 192). + */ + .vd_max = 192, + /* r is hardcoded to 22 and this is the actual divisor, +2 */ + .rd_min = 24, + .rd_max = 24, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, +}; + +static const struct icst_params icst525_ap_sys_params = { + .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, + /* Minimum 3 MHz, VDW = 4 */ + .vd_min = 3, + /* Maximum 50 MHz, VDW = 192 */ + .vd_max = 50, + /* r is hardcoded to 46 and this is the actual divisor, +2 */ + .rd_min = 48, + .rd_max = 48, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, +}; + +static const struct icst_params icst525_ap_pci_params = { + .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, + /* Minimum 25 MHz */ + .vd_min = 25, + /* Maximum 33 MHz */ + .vd_max = 33, + /* r is hardcoded to 14 or 22 and this is the actual divisors +2 */ + .rd_min = 16, + .rd_max = 24, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, +}; + static void __init of_syscon_icst_setup(struct device_node *np) { struct device_node *parent; @@ -247,6 +502,7 @@ static void __init of_syscon_icst_setup(struct device_node *np) const char *name = np->name; const char *parent_name; struct clk *regclk; + enum icst_control_type ctype; /* We do not release this reference, we are using it perpetually */ parent = of_get_parent(np); @@ -269,11 +525,28 @@ static void __init of_syscon_icst_setup(struct device_node *np) return; } - if (of_device_is_compatible(np, "arm,syscon-icst525")) + if (of_device_is_compatible(np, "arm,syscon-icst525")) { icst_desc.params = &icst525_params; - else if (of_device_is_compatible(np, "arm,syscon-icst307")) + ctype = ICST_VERSATILE; + } else if (of_device_is_compatible(np, "arm,syscon-icst307")) { icst_desc.params = &icst307_params; - else { + ctype = ICST_VERSATILE; + } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-cm")) { + icst_desc.params = &icst525_apcp_cm_params; + ctype = ICST_INTEGRATOR_AP_CM; + } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-sys")) { + icst_desc.params = &icst525_ap_sys_params; + ctype = ICST_INTEGRATOR_AP_SYS; + } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-pci")) { + icst_desc.params = &icst525_ap_pci_params; + ctype = ICST_INTEGRATOR_AP_PCI; + } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-core")) { + icst_desc.params = &icst525_apcp_cm_params; + ctype = ICST_INTEGRATOR_CP_CM_CORE; + } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-mem")) { + icst_desc.params = &icst525_apcp_cm_params; + ctype = ICST_INTEGRATOR_CP_CM_MEM; + } else { pr_err("unknown ICST clock %s\n", name); return; } @@ -281,7 +554,7 @@ static void __init of_syscon_icst_setup(struct device_node *np) /* Parent clock name is not the same as node parent */ parent_name = of_clk_get_parent_name(np, 0); - regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map); + regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map, ctype); if (IS_ERR(regclk)) { pr_err("error setting up syscon ICST clock %s\n", name); return; @@ -294,5 +567,14 @@ CLK_OF_DECLARE(arm_syscon_icst525_clk, "arm,syscon-icst525", of_syscon_icst_setup); CLK_OF_DECLARE(arm_syscon_icst307_clk, "arm,syscon-icst307", of_syscon_icst_setup); - +CLK_OF_DECLARE(arm_syscon_integratorap_cm_clk, + "arm,syscon-icst525-integratorap-cm", of_syscon_icst_setup); +CLK_OF_DECLARE(arm_syscon_integratorap_sys_clk, + "arm,syscon-icst525-integratorap-sys", of_syscon_icst_setup); +CLK_OF_DECLARE(arm_syscon_integratorap_pci_clk, + "arm,syscon-icst525-integratorap-pci", of_syscon_icst_setup); +CLK_OF_DECLARE(arm_syscon_integratorcp_cm_core_clk, + "arm,syscon-icst525-integratorcp-cm-core", of_syscon_icst_setup); +CLK_OF_DECLARE(arm_syscon_integratorcp_cm_mem_clk, + "arm,syscon-icst525-integratorcp-cm-mem", of_syscon_icst_setup); #endif diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile index 74005aa..83374bf 100644 --- a/drivers/clk/zte/Makefile +++ b/drivers/clk/zte/Makefile @@ -1,2 +1,3 @@ obj-y := clk.o obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o +obj-$(CONFIG_ARCH_ZX) += clk-zx296718.o diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c new file mode 100644 index 0000000..707d629 --- /dev/null +++ b/drivers/clk/zte/clk-zx296718.c @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2015 - 2016 ZTE Corporation. + * Copyright (C) 2016 Linaro Ltd. + * + * 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/device.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <dt-bindings/clock/zx296718-clock.h> +#include "clk.h" + +/* TOP CRM */ +#define TOP_CLK_MUX0 0x04 +#define TOP_CLK_MUX1 0x08 +#define TOP_CLK_MUX2 0x0c +#define TOP_CLK_MUX3 0x10 +#define TOP_CLK_MUX4 0x14 +#define TOP_CLK_MUX5 0x18 +#define TOP_CLK_MUX6 0x1c +#define TOP_CLK_MUX7 0x20 +#define TOP_CLK_MUX9 0x28 + + +#define TOP_CLK_GATE0 0x34 +#define TOP_CLK_GATE1 0x38 +#define TOP_CLK_GATE2 0x3c +#define TOP_CLK_GATE3 0x40 +#define TOP_CLK_GATE4 0x44 +#define TOP_CLK_GATE5 0x48 +#define TOP_CLK_GATE6 0x4c + +#define TOP_CLK_DIV0 0x58 + +#define PLL_CPU_REG 0x80 +#define PLL_VGA_REG 0xb0 +#define PLL_DDR_REG 0xa0 + +/* LSP0 CRM */ +#define LSP0_TIMER3_CLK 0x4 +#define LSP0_TIMER4_CLK 0x8 +#define LSP0_TIMER5_CLK 0xc +#define LSP0_UART3_CLK 0x10 +#define LSP0_UART1_CLK 0x14 +#define LSP0_UART2_CLK 0x18 +#define LSP0_SPIFC0_CLK 0x1c +#define LSP0_I2C4_CLK 0x20 +#define LSP0_I2C5_CLK 0x24 +#define LSP0_SSP0_CLK 0x28 +#define LSP0_SSP1_CLK 0x2c +#define LSP0_USIM0_CLK 0x30 +#define LSP0_GPIO_CLK 0x34 +#define LSP0_I2C3_CLK 0x38 + +/* LSP1 CRM */ +#define LSP1_UART4_CLK 0x08 +#define LSP1_UART5_CLK 0x0c +#define LSP1_PWM_CLK 0x10 +#define LSP1_I2C2_CLK 0x14 +#define LSP1_SSP2_CLK 0x1c +#define LSP1_SSP3_CLK 0x20 +#define LSP1_SSP4_CLK 0x24 +#define LSP1_USIM1_CLK 0x28 + +/* audio lsp */ +#define AUDIO_I2S0_DIV_CFG1 0x10 +#define AUDIO_I2S0_DIV_CFG2 0x14 +#define AUDIO_I2S0_CLK 0x18 +#define AUDIO_I2S1_DIV_CFG1 0x20 +#define AUDIO_I2S1_DIV_CFG2 0x24 +#define AUDIO_I2S1_CLK 0x28 +#define AUDIO_I2S2_DIV_CFG1 0x30 +#define AUDIO_I2S2_DIV_CFG2 0x34 +#define AUDIO_I2S2_CLK 0x38 +#define AUDIO_I2S3_DIV_CFG1 0x40 +#define AUDIO_I2S3_DIV_CFG2 0x44 +#define AUDIO_I2S3_CLK 0x48 +#define AUDIO_I2C0_CLK 0x50 +#define AUDIO_SPDIF0_DIV_CFG1 0x60 +#define AUDIO_SPDIF0_DIV_CFG2 0x64 +#define AUDIO_SPDIF0_CLK 0x68 +#define AUDIO_SPDIF1_DIV_CFG1 0x70 +#define AUDIO_SPDIF1_DIV_CFG2 0x74 +#define AUDIO_SPDIF1_CLK 0x78 +#define AUDIO_TIMER_CLK 0x80 +#define AUDIO_TDM_CLK 0x90 +#define AUDIO_TS_CLK 0xa0 + +static DEFINE_SPINLOCK(clk_lock); + +static struct zx_pll_config pll_cpu_table[] = { + PLL_RATE(1312000000, 0x00103621, 0x04aaaaaa), + PLL_RATE(1407000000, 0x00103a21, 0x04aaaaaa), + PLL_RATE(1503000000, 0x00103e21, 0x04aaaaaa), + PLL_RATE(1600000000, 0x00104221, 0x04aaaaaa), +}; + +PNAME(osc) = { + "osc24m", + "osc32k", +}; + +PNAME(dbg_wclk_p) = { + "clk334m", + "clk466m", + "clk396m", + "clk250m", +}; + +PNAME(a72_coreclk_p) = { + "osc24m", + "pll_mm0_1188m", + "pll_mm1_1296m", + "clk1000m", + "clk648m", + "clk1600m", + "pll_audio_1800m", + "pll_vga_1800m", +}; + +PNAME(cpu_periclk_p) = { + "osc24m", + "clk500m", + "clk594m", + "clk466m", + "clk294m", + "clk334m", + "clk250m", + "clk125m", +}; + +PNAME(a53_coreclk_p) = { + "osc24m", + "clk1000m", + "pll_mm0_1188m", + "clk648m", + "clk500m", + "clk800m", + "clk1600m", + "pll_audio_1800m", +}; + +PNAME(sec_wclk_p) = { + "osc24m", + "clk396m", + "clk334m", + "clk297m", + "clk250m", + "clk198m", + "clk148m5", + "clk99m", +}; + +PNAME(sd_nand_wclk_p) = { + "osc24m", + "clk49m5", + "clk99m", + "clk198m", + "clk167m", + "clk148m5", + "clk125m", + "clk216m", +}; + +PNAME(emmc_wclk_p) = { + "osc24m", + "clk198m", + "clk99m", + "clk396m", + "clk334m", + "clk297m", + "clk250m", + "clk148m5", +}; + +PNAME(clk32_p) = { + "osc32k", + "clk32k768", +}; + +PNAME(usb_ref24m_p) = { + "osc32k", + "clk32k768", +}; + +PNAME(sys_noc_alck_p) = { + "osc24m", + "clk250m", + "clk198m", + "clk148m5", + "clk108m", + "clk54m", + "clk216m", + "clk240m", +}; + +PNAME(vde_aclk_p) = { + "clk334m", + "clk594m", + "clk500m", + "clk432m", + "clk480m", + "clk297m", + "clk_vga", /*600MHz*/ + "clk294m", +}; + +PNAME(vce_aclk_p) = { + "clk334m", + "clk594m", + "clk500m", + "clk432m", + "clk396m", + "clk297m", + "clk_vga", /*600MHz*/ + "clk294m", +}; + +PNAME(hde_aclk_p) = { + "clk334m", + "clk594m", + "clk500m", + "clk432m", + "clk396m", + "clk297m", + "clk_vga", /*600MHz*/ + "clk294m", +}; + +PNAME(gpu_aclk_p) = { + "clk334m", + "clk648m", + "clk594m", + "clk500m", + "clk396m", + "clk297m", + "clk_vga", /*600MHz*/ + "clk294m", +}; + +PNAME(sappu_aclk_p) = { + "clk396m", + "clk500m", + "clk250m", + "clk148m5", +}; + +PNAME(sappu_wclk_p) = { + "clk198m", + "clk396m", + "clk334m", + "clk297m", + "clk250m", + "clk148m5", + "clk125m", + "clk99m", +}; + +PNAME(vou_aclk_p) = { + "clk334m", + "clk594m", + "clk500m", + "clk432m", + "clk396m", + "clk297m", + "clk_vga", /*600MHz*/ + "clk294m", +}; + +PNAME(vou_main_wclk_p) = { + "clk108m", + "clk594m", + "clk297m", + "clk148m5", + "clk74m25", + "clk54m", + "clk27m", + "clk_vga", +}; + +PNAME(vou_aux_wclk_p) = { + "clk108m", + "clk148m5", + "clk74m25", + "clk54m", + "clk27m", + "clk_vga", + "clk54m_mm0", + "clk" +}; + +PNAME(vou_ppu_wclk_p) = { + "clk334m", + "clk432m", + "clk396m", + "clk297m", + "clk250m", + "clk125m", + "clk198m", + "clk99m", +}; + +PNAME(vga_i2c_wclk_p) = { + "osc24m", + "clk99m", +}; + +PNAME(viu_m0_aclk_p) = { + "clk334m", + "clk432m", + "clk396m", + "clk297m", + "clk250m", + "clk125m", + "clk198m", + "osc24m", +}; + +PNAME(viu_m1_aclk_p) = { + "clk198m", + "clk250m", + "clk297m", + "clk125m", + "clk396m", + "clk334m", + "clk148m5", + "osc24m", +}; + +PNAME(viu_clk_p) = { + "clk198m", + "clk334m", + "clk297m", + "clk250m", + "clk396m", + "clk125m", + "clk99m", + "clk148m5", +}; + +PNAME(viu_jpeg_clk_p) = { + "clk334m", + "clk480m", + "clk432m", + "clk396m", + "clk297m", + "clk250m", + "clk125m", + "clk198m", +}; + +PNAME(ts_sys_clk_p) = { + "clk192m", + "clk167m", + "clk125m", + "clk99m", +}; + +PNAME(wdt_ares_p) = { + "osc24m", + "clk32k" +}; + +static struct clk_zx_pll zx296718_pll_clk[] = { + ZX296718_PLL("pll_cpu", "osc24m", PLL_CPU_REG, pll_cpu_table), +}; + +static struct zx_clk_fixed_factor top_ffactor_clk[] = { + FFACTOR(0, "clk4m", "osc24m", 1, 6, 0), + FFACTOR(0, "clk2m", "osc24m", 1, 12, 0), + /* pll cpu */ + FFACTOR(0, "clk1600m", "pll_cpu", 1, 1, CLK_SET_RATE_PARENT), + FFACTOR(0, "clk800m", "pll_cpu", 1, 2, CLK_SET_RATE_PARENT), + /* pll mac */ + FFACTOR(0, "clk25m", "pll_mac", 1, 40, 0), + FFACTOR(0, "clk125m", "pll_mac", 1, 8, 0), + FFACTOR(0, "clk250m", "pll_mac", 1, 4, 0), + FFACTOR(0, "clk50m", "pll_mac", 1, 20, 0), + FFACTOR(0, "clk500m", "pll_mac", 1, 2, 0), + FFACTOR(0, "clk1000m", "pll_mac", 1, 1, 0), + FFACTOR(0, "clk334m", "pll_mac", 1, 3, 0), + FFACTOR(0, "clk167m", "pll_mac", 1, 6, 0), + /* pll mm */ + FFACTOR(0, "clk54m_mm0", "pll_mm0", 1, 22, 0), + FFACTOR(0, "clk74m25", "pll_mm0", 1, 16, 0), + FFACTOR(0, "clk148m5", "pll_mm0", 1, 8, 0), + FFACTOR(0, "clk297m", "pll_mm0", 1, 4, 0), + FFACTOR(0, "clk594m", "pll_mm0", 1, 2, 0), + FFACTOR(0, "pll_mm0_1188m", "pll_mm0", 1, 1, 0), + FFACTOR(0, "clk396m", "pll_mm0", 1, 3, 0), + FFACTOR(0, "clk198m", "pll_mm0", 1, 6, 0), + FFACTOR(0, "clk99m", "pll_mm0", 1, 12, 0), + FFACTOR(0, "clk49m5", "pll_mm0", 1, 24, 0), + /* pll mm */ + FFACTOR(0, "clk324m", "pll_mm1", 1, 4, 0), + FFACTOR(0, "clk648m", "pll_mm1", 1, 2, 0), + FFACTOR(0, "pll_mm1_1296m", "pll_mm1", 1, 1, 0), + FFACTOR(0, "clk216m", "pll_mm1", 1, 6, 0), + FFACTOR(0, "clk432m", "pll_mm1", 1, 3, 0), + FFACTOR(0, "clk108m", "pll_mm1", 1, 12, 0), + FFACTOR(0, "clk72m", "pll_mm1", 1, 18, 0), + FFACTOR(0, "clk27m", "pll_mm1", 1, 48, 0), + FFACTOR(0, "clk54m", "pll_mm1", 1, 24, 0), + /* vga */ + FFACTOR(0, "pll_vga_1800m", "pll_vga", 1, 1, 0), + FFACTOR(0, "clk_vga", "pll_vga", 1, 2, 0), + /* pll ddr */ + FFACTOR(0, "clk466m", "pll_ddr", 1, 2, 0), + + /* pll audio */ + FFACTOR(0, "pll_audio_1800m", "pll_audio", 1, 1, 0), + FFACTOR(0, "clk32k768", "pll_audio", 1, 27000, 0), + FFACTOR(0, "clk16m384", "pll_audio", 1, 54, 0), + FFACTOR(0, "clk294m", "pll_audio", 1, 3, 0), + + /* pll hsic*/ + FFACTOR(0, "clk240m", "pll_hsic", 1, 4, 0), + FFACTOR(0, "clk480m", "pll_hsic", 1, 2, 0), + FFACTOR(0, "clk192m", "pll_hsic", 1, 5, 0), + FFACTOR(0, "clk_pll_24m", "pll_hsic", 1, 40, 0), + FFACTOR(0, "emmc_mux_div2", "emmc_mux", 1, 2, CLK_SET_RATE_PARENT), +}; + +static struct clk_div_table noc_div_table[] = { + { .val = 1, .div = 2, }, + { .val = 3, .div = 4, }, +}; +static struct zx_clk_div top_div_clk[] = { + DIV_T(0, "sys_noc_hclk", "sys_noc_aclk", TOP_CLK_DIV0, 0, 2, 0, noc_div_table), + DIV_T(0, "sys_noc_pclk", "sys_noc_aclk", TOP_CLK_DIV0, 4, 2, 0, noc_div_table), +}; + +static struct zx_clk_mux top_mux_clk[] = { + MUX(0, "dbg_mux", dbg_wclk_p, TOP_CLK_MUX0, 12, 2), + MUX(0, "a72_mux", a72_coreclk_p, TOP_CLK_MUX0, 8, 3), + MUX(0, "cpu_peri_mux", cpu_periclk_p, TOP_CLK_MUX0, 4, 3), + MUX_F(0, "a53_mux", a53_coreclk_p, TOP_CLK_MUX0, 0, 3, CLK_SET_RATE_PARENT, 0), + MUX(0, "sys_noc_aclk", sys_noc_alck_p, TOP_CLK_MUX1, 0, 3), + MUX(0, "sec_mux", sec_wclk_p, TOP_CLK_MUX2, 16, 3), + MUX(0, "sd1_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 12, 3), + MUX(0, "sd0_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 8, 3), + MUX(0, "emmc_mux", emmc_wclk_p, TOP_CLK_MUX2, 4, 3), + MUX(0, "nand_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 0, 3), + MUX(0, "usb_ref24m_mux", usb_ref24m_p, TOP_CLK_MUX9, 16, 1), + MUX(0, "clk32k", clk32_p, TOP_CLK_MUX9, 12, 1), + MUX_F(0, "wdt_mux", wdt_ares_p, TOP_CLK_MUX9, 8, 1, CLK_SET_RATE_PARENT, 0), + MUX(0, "timer_mux", osc, TOP_CLK_MUX9, 4, 1), + MUX(0, "vde_mux", vde_aclk_p, TOP_CLK_MUX4, 0, 3), + MUX(0, "vce_mux", vce_aclk_p, TOP_CLK_MUX4, 4, 3), + MUX(0, "hde_mux", hde_aclk_p, TOP_CLK_MUX4, 8, 3), + MUX(0, "gpu_mux", gpu_aclk_p, TOP_CLK_MUX5, 0, 3), + MUX(0, "sappu_a_mux", sappu_aclk_p, TOP_CLK_MUX5, 4, 2), + MUX(0, "sappu_w_mux", sappu_wclk_p, TOP_CLK_MUX5, 8, 3), + MUX(0, "vou_a_mux", vou_aclk_p, TOP_CLK_MUX7, 0, 3), + MUX(0, "vou_main_w_mux", vou_main_wclk_p, TOP_CLK_MUX7, 4, 3), + MUX(0, "vou_aux_w_mux", vou_aux_wclk_p, TOP_CLK_MUX7, 8, 3), + MUX(0, "vou_ppu_w_mux", vou_ppu_wclk_p, TOP_CLK_MUX7, 12, 3), + MUX(0, "vga_i2c_mux", vga_i2c_wclk_p, TOP_CLK_MUX7, 16, 1), + MUX(0, "viu_m0_a_mux", viu_m0_aclk_p, TOP_CLK_MUX6, 0, 3), + MUX(0, "viu_m1_a_mux", viu_m1_aclk_p, TOP_CLK_MUX6, 4, 3), + MUX(0, "viu_w_mux", viu_clk_p, TOP_CLK_MUX6, 8, 3), + MUX(0, "viu_jpeg_w_mux", viu_jpeg_clk_p, TOP_CLK_MUX6, 12, 3), + MUX(0, "ts_sys_mux", ts_sys_clk_p, TOP_CLK_MUX6, 16, 2), +}; + +static struct zx_clk_gate top_gate_clk[] = { + GATE(CPU_DBG_GATE, "dbg_wclk", "dbg_mux", TOP_CLK_GATE0, 4, CLK_SET_RATE_PARENT, 0), + GATE(A72_GATE, "a72_coreclk", "a72_mux", TOP_CLK_GATE0, 3, CLK_SET_RATE_PARENT, 0), + GATE(CPU_PERI_GATE, "cpu_peri", "cpu_peri_mux", TOP_CLK_GATE0, 1, CLK_SET_RATE_PARENT, 0), + GATE(A53_GATE, "a53_coreclk", "a53_mux", TOP_CLK_GATE0, 0, CLK_SET_RATE_PARENT, 0), + GATE(SD1_WCLK, "sd1_wclk", "sd1_mux", TOP_CLK_GATE1, 13, CLK_SET_RATE_PARENT, 0), + GATE(SD0_WCLK, "sd0_wclk", "sd0_mux", TOP_CLK_GATE1, 9, CLK_SET_RATE_PARENT, 0), + GATE(EMMC_WCLK, "emmc_wclk", "emmc_mux_div2", TOP_CLK_GATE0, 5, CLK_SET_RATE_PARENT, 0), + GATE(EMMC_NAND_AXI, "emmc_nand_aclk", "sys_noc_aclk", TOP_CLK_GATE1, 4, CLK_SET_RATE_PARENT, 0), + GATE(NAND_WCLK, "nand_wclk", "nand_mux", TOP_CLK_GATE0, 1, CLK_SET_RATE_PARENT, 0), + GATE(EMMC_NAND_AHB, "emmc_nand_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 0, CLK_SET_RATE_PARENT, 0), + GATE(0, "lsp1_pclk", "sys_noc_pclk", TOP_CLK_GATE2, 31, 0, 0), + GATE(LSP1_148M5, "lsp1_148m5", "clk148m5", TOP_CLK_GATE2, 30, 0, 0), + GATE(LSP1_99M, "lsp1_99m", "clk99m", TOP_CLK_GATE2, 29, 0, 0), + GATE(LSP1_24M, "lsp1_24m", "osc24m", TOP_CLK_GATE2, 28, 0, 0), + GATE(LSP0_74M25, "lsp0_74m25", "clk74m25", TOP_CLK_GATE2, 25, 0, 0), + GATE(0, "lsp0_pclk", "sys_noc_pclk", TOP_CLK_GATE2, 24, 0, 0), + GATE(LSP0_32K, "lsp0_32k", "osc32k", TOP_CLK_GATE2, 23, 0, 0), + GATE(LSP0_148M5, "lsp0_148m5", "clk148m5", TOP_CLK_GATE2, 22, 0, 0), + GATE(LSP0_99M, "lsp0_99m", "clk99m", TOP_CLK_GATE2, 21, 0, 0), + GATE(LSP0_24M, "lsp0_24m", "osc24m", TOP_CLK_GATE2, 20, 0, 0), + GATE(AUDIO_99M, "audio_99m", "clk99m", TOP_CLK_GATE5, 27, 0, 0), + GATE(AUDIO_24M, "audio_24m", "osc24m", TOP_CLK_GATE5, 28, 0, 0), + GATE(AUDIO_16M384, "audio_16m384", "clk16m384", TOP_CLK_GATE5, 29, 0, 0), + GATE(AUDIO_32K, "audio_32k", "clk32k", TOP_CLK_GATE5, 30, 0, 0), + GATE(WDT_WCLK, "wdt_wclk", "wdt_mux", TOP_CLK_GATE6, 9, CLK_SET_RATE_PARENT, 0), + GATE(TIMER_WCLK, "timer_wclk", "timer_mux", TOP_CLK_GATE6, 5, CLK_SET_RATE_PARENT, 0), + GATE(VDE_ACLK, "vde_aclk", "vde_mux", TOP_CLK_GATE3, 0, CLK_SET_RATE_PARENT, 0), + GATE(VCE_ACLK, "vce_aclk", "vce_mux", TOP_CLK_GATE3, 4, CLK_SET_RATE_PARENT, 0), + GATE(HDE_ACLK, "hde_aclk", "hde_mux", TOP_CLK_GATE3, 8, CLK_SET_RATE_PARENT, 0), + GATE(GPU_ACLK, "gpu_aclk", "gpu_mux", TOP_CLK_GATE3, 16, CLK_SET_RATE_PARENT, 0), + GATE(SAPPU_ACLK, "sappu_aclk", "sappu_a_mux", TOP_CLK_GATE3, 20, CLK_SET_RATE_PARENT, 0), + GATE(SAPPU_WCLK, "sappu_wclk", "sappu_w_mux", TOP_CLK_GATE3, 22, CLK_SET_RATE_PARENT, 0), + GATE(VOU_ACLK, "vou_aclk", "vou_a_mux", TOP_CLK_GATE4, 16, CLK_SET_RATE_PARENT, 0), + GATE(VOU_MAIN_WCLK, "vou_main_wclk", "vou_main_w_mux", TOP_CLK_GATE4, 18, CLK_SET_RATE_PARENT, 0), + GATE(VOU_AUX_WCLK, "vou_aux_wclk", "vou_aux_w_mux", TOP_CLK_GATE4, 19, CLK_SET_RATE_PARENT, 0), + GATE(VOU_PPU_WCLK, "vou_ppu_wclk", "vou_ppu_w_mux", TOP_CLK_GATE4, 20, CLK_SET_RATE_PARENT, 0), + GATE(MIPI_CFG_CLK, "mipi_cfg_clk", "osc24m", TOP_CLK_GATE4, 21, 0, 0), + GATE(VGA_I2C_WCLK, "vga_i2c_wclk", "vga_i2c_mux", TOP_CLK_GATE4, 23, CLK_SET_RATE_PARENT, 0), + GATE(MIPI_REF_CLK, "mipi_ref_clk", "clk27m", TOP_CLK_GATE4, 24, 0, 0), + GATE(HDMI_OSC_CEC, "hdmi_osc_cec", "clk2m", TOP_CLK_GATE4, 22, 0, 0), + GATE(HDMI_OSC_CLK, "hdmi_osc_clk", "clk240m", TOP_CLK_GATE4, 25, 0, 0), + GATE(HDMI_XCLK, "hdmi_xclk", "osc24m", TOP_CLK_GATE4, 26, 0, 0), + GATE(VIU_M0_ACLK, "viu_m0_aclk", "viu_m0_a_mux", TOP_CLK_GATE4, 0, CLK_SET_RATE_PARENT, 0), + GATE(VIU_M1_ACLK, "viu_m1_aclk", "viu_m1_a_mux", TOP_CLK_GATE4, 1, CLK_SET_RATE_PARENT, 0), + GATE(VIU_WCLK, "viu_wclk", "viu_w_mux", TOP_CLK_GATE4, 2, CLK_SET_RATE_PARENT, 0), + GATE(VIU_JPEG_WCLK, "viu_jpeg_wclk", "viu_jpeg_w_mux", TOP_CLK_GATE4, 3, CLK_SET_RATE_PARENT, 0), + GATE(VIU_CFG_CLK, "viu_cfg_clk", "osc24m", TOP_CLK_GATE4, 6, 0, 0), + GATE(TS_SYS_WCLK, "ts_sys_wclk", "ts_sys_mux", TOP_CLK_GATE5, 2, CLK_SET_RATE_PARENT, 0), + GATE(TS_SYS_108M, "ts_sys_108m", "clk108m", TOP_CLK_GATE5, 3, 0, 0), + GATE(USB20_HCLK, "usb20_hclk", "sys_noc_hclk", TOP_CLK_GATE2, 12, 0, 0), + GATE(USB20_PHY_CLK, "usb20_phy_clk", "usb_ref24m_mux", TOP_CLK_GATE2, 13, 0, 0), + GATE(USB21_HCLK, "usb21_hclk", "sys_noc_hclk", TOP_CLK_GATE2, 14, 0, 0), + GATE(USB21_PHY_CLK, "usb21_phy_clk", "usb_ref24m_mux", TOP_CLK_GATE2, 15, 0, 0), + GATE(GMAC_RMIICLK, "gmac_rmii_clk", "clk50m", TOP_CLK_GATE2, 3, 0, 0), + GATE(GMAC_PCLK, "gmac_pclk", "clk198m", TOP_CLK_GATE2, 1, 0, 0), + GATE(GMAC_ACLK, "gmac_aclk", "clk49m5", TOP_CLK_GATE2, 0, 0, 0), + GATE(GMAC_RFCLK, "gmac_refclk", "clk25m", TOP_CLK_GATE2, 4, 0, 0), + GATE(SD1_AHB, "sd1_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 12, 0, 0), + GATE(SD0_AHB, "sd0_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 8, 0, 0), + GATE(TEMPSENSOR_GATE, "tempsensor_gate", "clk4m", TOP_CLK_GATE5, 31, 0, 0), +}; + +static struct clk_hw_onecell_data top_hw_onecell_data = { + .num = TOP_NR_CLKS, + .hws = { + [TOP_NR_CLKS - 1] = NULL, + }, +}; + +static int __init top_clocks_init(struct device_node *np) +{ + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return -ENXIO; + } + + for (i = 0; i < ARRAY_SIZE(zx296718_pll_clk); i++) { + zx296718_pll_clk[i].reg_base += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &zx296718_pll_clk[i].hw); + if (ret) { + pr_warn("top clk %s init error!\n", + zx296718_pll_clk[i].hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(top_ffactor_clk); i++) { + if (top_ffactor_clk[i].id) + top_hw_onecell_data.hws[top_ffactor_clk[i].id] = + &top_ffactor_clk[i].factor.hw; + + ret = clk_hw_register(NULL, &top_ffactor_clk[i].factor.hw); + if (ret) { + pr_warn("top clk %s init error!\n", + top_ffactor_clk[i].factor.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(top_mux_clk); i++) { + if (top_mux_clk[i].id) + top_hw_onecell_data.hws[top_mux_clk[i].id] = + &top_mux_clk[i].mux.hw; + + top_mux_clk[i].mux.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &top_mux_clk[i].mux.hw); + if (ret) { + pr_warn("top clk %s init error!\n", + top_mux_clk[i].mux.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(top_gate_clk); i++) { + if (top_gate_clk[i].id) + top_hw_onecell_data.hws[top_gate_clk[i].id] = + &top_gate_clk[i].gate.hw; + + top_gate_clk[i].gate.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &top_gate_clk[i].gate.hw); + if (ret) { + pr_warn("top clk %s init error!\n", + top_gate_clk[i].gate.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(top_div_clk); i++) { + if (top_div_clk[i].id) + top_hw_onecell_data.hws[top_div_clk[i].id] = + &top_div_clk[i].div.hw; + + top_div_clk[i].div.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &top_div_clk[i].div.hw); + if (ret) { + pr_warn("top clk %s init error!\n", + top_div_clk[i].div.hw.init->name); + } + } + + if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &top_hw_onecell_data)) + panic("could not register clk provider\n"); + pr_info("top clk init over, nr:%d\n", TOP_NR_CLKS); + + return 0; +} + +static struct clk_div_table common_even_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 3, .div = 4, }, + { .val = 5, .div = 6, }, + { .val = 7, .div = 8, }, + { .val = 9, .div = 10, }, + { .val = 11, .div = 12, }, + { .val = 13, .div = 14, }, + { .val = 15, .div = 16, }, +}; + +static struct clk_div_table common_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 3, }, + { .val = 3, .div = 4, }, + { .val = 4, .div = 5, }, + { .val = 5, .div = 6, }, + { .val = 6, .div = 7, }, + { .val = 7, .div = 8, }, + { .val = 8, .div = 9, }, + { .val = 9, .div = 10, }, + { .val = 10, .div = 11, }, + { .val = 11, .div = 12, }, + { .val = 12, .div = 13, }, + { .val = 13, .div = 14, }, + { .val = 14, .div = 15, }, + { .val = 15, .div = 16, }, +}; + +PNAME(lsp0_wclk_common_p) = { + "lsp0_24m", + "lsp0_99m", +}; + +PNAME(lsp0_wclk_timer3_p) = { + "timer3_div", + "lsp0_32k" +}; + +PNAME(lsp0_wclk_timer4_p) = { + "timer4_div", + "lsp0_32k" +}; + +PNAME(lsp0_wclk_timer5_p) = { + "timer5_div", + "lsp0_32k" +}; + +PNAME(lsp0_wclk_spifc0_p) = { + "lsp0_148m5", + "lsp0_24m", + "lsp0_99m", + "lsp0_74m25" +}; + +PNAME(lsp0_wclk_ssp_p) = { + "lsp0_148m5", + "lsp0_99m", + "lsp0_24m", +}; + +static struct zx_clk_mux lsp0_mux_clk[] = { + MUX(0, "timer3_wclk_mux", lsp0_wclk_timer3_p, LSP0_TIMER3_CLK, 4, 1), + MUX(0, "timer4_wclk_mux", lsp0_wclk_timer4_p, LSP0_TIMER4_CLK, 4, 1), + MUX(0, "timer5_wclk_mux", lsp0_wclk_timer5_p, LSP0_TIMER5_CLK, 4, 1), + MUX(0, "uart3_wclk_mux", lsp0_wclk_common_p, LSP0_UART3_CLK, 4, 1), + MUX(0, "uart1_wclk_mux", lsp0_wclk_common_p, LSP0_UART1_CLK, 4, 1), + MUX(0, "uart2_wclk_mux", lsp0_wclk_common_p, LSP0_UART2_CLK, 4, 1), + MUX(0, "spifc0_wclk_mux", lsp0_wclk_spifc0_p, LSP0_SPIFC0_CLK, 4, 2), + MUX(0, "i2c4_wclk_mux", lsp0_wclk_common_p, LSP0_I2C4_CLK, 4, 1), + MUX(0, "i2c5_wclk_mux", lsp0_wclk_common_p, LSP0_I2C5_CLK, 4, 1), + MUX(0, "ssp0_wclk_mux", lsp0_wclk_ssp_p, LSP0_SSP0_CLK, 4, 1), + MUX(0, "ssp1_wclk_mux", lsp0_wclk_ssp_p, LSP0_SSP1_CLK, 4, 1), + MUX(0, "i2c3_wclk_mux", lsp0_wclk_common_p, LSP0_I2C3_CLK, 4, 1), +}; + +static struct zx_clk_gate lsp0_gate_clk[] = { + GATE(LSP0_TIMER3_WCLK, "timer3_wclk", "timer3_wclk_mux", LSP0_TIMER3_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_TIMER4_WCLK, "timer4_wclk", "timer4_wclk_mux", LSP0_TIMER4_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_TIMER5_WCLK, "timer5_wclk", "timer5_wclk_mux", LSP0_TIMER5_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_UART3_WCLK, "uart3_wclk", "uart3_wclk_mux", LSP0_UART3_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_UART1_WCLK, "uart1_wclk", "uart1_wclk_mux", LSP0_UART1_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_UART2_WCLK, "uart2_wclk", "uart2_wclk_mux", LSP0_UART2_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_SPIFC0_WCLK, "spifc0_wclk", "spifc0_wclk_mux", LSP0_SPIFC0_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_I2C4_WCLK, "i2c4_wclk", "i2c4_wclk_mux", LSP0_I2C4_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_I2C5_WCLK, "i2c5_wclk", "i2c5_wclk_mux", LSP0_I2C5_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_SSP0_WCLK, "ssp0_wclk", "ssp0_div", LSP0_SSP0_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_SSP1_WCLK, "ssp1_wclk", "ssp1_div", LSP0_SSP1_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP0_I2C3_WCLK, "i2c3_wclk", "i2c3_wclk_mux", LSP0_I2C3_CLK, 1, CLK_SET_RATE_PARENT, 0), +}; + +static struct zx_clk_div lsp0_div_clk[] = { + DIV_T(0, "timer3_div", "lsp0_24m", LSP0_TIMER3_CLK, 12, 4, 0, common_even_div_table), + DIV_T(0, "timer4_div", "lsp0_24m", LSP0_TIMER4_CLK, 12, 4, 0, common_even_div_table), + DIV_T(0, "timer5_div", "lsp0_24m", LSP0_TIMER5_CLK, 12, 4, 0, common_even_div_table), + DIV_T(0, "ssp0_div", "ssp0_wclk_mux", LSP0_SSP0_CLK, 12, 4, 0, common_even_div_table), + DIV_T(0, "ssp1_div", "ssp1_wclk_mux", LSP0_SSP1_CLK, 12, 4, 0, common_even_div_table), +}; + +static struct clk_hw_onecell_data lsp0_hw_onecell_data = { + .num = LSP0_NR_CLKS, + .hws = { + [LSP0_NR_CLKS - 1] = NULL, + }, +}; + +static int __init lsp0_clocks_init(struct device_node *np) +{ + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return -ENXIO; + } + + for (i = 0; i < ARRAY_SIZE(lsp0_mux_clk); i++) { + if (lsp0_mux_clk[i].id) + lsp0_hw_onecell_data.hws[lsp0_mux_clk[i].id] = + &lsp0_mux_clk[i].mux.hw; + + lsp0_mux_clk[i].mux.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp0_mux_clk[i].mux.hw); + if (ret) { + pr_warn("lsp0 clk %s init error!\n", + lsp0_mux_clk[i].mux.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(lsp0_gate_clk); i++) { + if (lsp0_gate_clk[i].id) + lsp0_hw_onecell_data.hws[lsp0_gate_clk[i].id] = + &lsp0_gate_clk[i].gate.hw; + + lsp0_gate_clk[i].gate.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp0_gate_clk[i].gate.hw); + if (ret) { + pr_warn("lsp0 clk %s init error!\n", + lsp0_gate_clk[i].gate.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(lsp0_div_clk); i++) { + if (lsp0_div_clk[i].id) + lsp0_hw_onecell_data.hws[lsp0_div_clk[i].id] = + &lsp0_div_clk[i].div.hw; + + lsp0_div_clk[i].div.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp0_div_clk[i].div.hw); + if (ret) { + pr_warn("lsp0 clk %s init error!\n", + lsp0_div_clk[i].div.hw.init->name); + } + } + + if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp0_hw_onecell_data)) + panic("could not register clk provider\n"); + pr_info("lsp0-clk init over:%d\n", LSP0_NR_CLKS); + + return 0; +} + +PNAME(lsp1_wclk_common_p) = { + "lsp1_24m", + "lsp1_99m", +}; + +PNAME(lsp1_wclk_ssp_p) = { + "lsp1_148m5", + "lsp1_99m", + "lsp1_24m", +}; + +static struct zx_clk_mux lsp1_mux_clk[] = { + MUX(0, "uart4_wclk_mux", lsp1_wclk_common_p, LSP1_UART4_CLK, 4, 1), + MUX(0, "uart5_wclk_mux", lsp1_wclk_common_p, LSP1_UART5_CLK, 4, 1), + MUX(0, "pwm_wclk_mux", lsp1_wclk_common_p, LSP1_PWM_CLK, 4, 1), + MUX(0, "i2c2_wclk_mux", lsp1_wclk_common_p, LSP1_I2C2_CLK, 4, 1), + MUX(0, "ssp2_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP2_CLK, 4, 2), + MUX(0, "ssp3_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP3_CLK, 4, 2), + MUX(0, "ssp4_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP4_CLK, 4, 2), + MUX(0, "usim1_wclk_mux", lsp1_wclk_common_p, LSP1_USIM1_CLK, 4, 1), +}; + +static struct zx_clk_div lsp1_div_clk[] = { + DIV_T(0, "pwm_div", "pwm_wclk_mux", LSP1_PWM_CLK, 12, 4, CLK_SET_RATE_PARENT, common_div_table), + DIV_T(0, "ssp2_div", "ssp2_wclk_mux", LSP1_SSP2_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table), + DIV_T(0, "ssp3_div", "ssp3_wclk_mux", LSP1_SSP3_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table), + DIV_T(0, "ssp4_div", "ssp4_wclk_mux", LSP1_SSP4_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table), +}; + +static struct zx_clk_gate lsp1_gate_clk[] = { + GATE(LSP1_UART4_WCLK, "lsp1_uart4_wclk", "uart4_wclk_mux", LSP1_UART4_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_UART5_WCLK, "lsp1_uart5_wclk", "uart5_wclk_mux", LSP1_UART5_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_PWM_WCLK, "lsp1_pwm_wclk", "pwm_div", LSP1_PWM_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_PWM_PCLK, "lsp1_pwm_pclk", "lsp1_pclk", LSP1_PWM_CLK, 0, 0, 0), + GATE(LSP1_I2C2_WCLK, "lsp1_i2c2_wclk", "i2c2_wclk_mux", LSP1_I2C2_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_SSP2_WCLK, "lsp1_ssp2_wclk", "ssp2_div", LSP1_SSP2_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_SSP3_WCLK, "lsp1_ssp3_wclk", "ssp3_div", LSP1_SSP3_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_SSP4_WCLK, "lsp1_ssp4_wclk", "ssp4_div", LSP1_SSP4_CLK, 1, CLK_SET_RATE_PARENT, 0), + GATE(LSP1_USIM1_WCLK, "lsp1_usim1_wclk", "usim1_wclk_mux", LSP1_USIM1_CLK, 1, CLK_SET_RATE_PARENT, 0), +}; + +static struct clk_hw_onecell_data lsp1_hw_onecell_data = { + .num = LSP1_NR_CLKS, + .hws = { + [LSP1_NR_CLKS - 1] = NULL, + }, +}; + +static int __init lsp1_clocks_init(struct device_node *np) +{ + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return -ENXIO; + } + + for (i = 0; i < ARRAY_SIZE(lsp1_mux_clk); i++) { + if (lsp1_mux_clk[i].id) + lsp1_hw_onecell_data.hws[lsp1_mux_clk[i].id] = + &lsp0_mux_clk[i].mux.hw; + + lsp1_mux_clk[i].mux.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp1_mux_clk[i].mux.hw); + if (ret) { + pr_warn("lsp1 clk %s init error!\n", + lsp1_mux_clk[i].mux.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(lsp1_gate_clk); i++) { + if (lsp1_gate_clk[i].id) + lsp1_hw_onecell_data.hws[lsp1_gate_clk[i].id] = + &lsp1_gate_clk[i].gate.hw; + + lsp1_gate_clk[i].gate.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp1_gate_clk[i].gate.hw); + if (ret) { + pr_warn("lsp1 clk %s init error!\n", + lsp1_gate_clk[i].gate.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(lsp1_div_clk); i++) { + if (lsp1_div_clk[i].id) + lsp1_hw_onecell_data.hws[lsp1_div_clk[i].id] = + &lsp1_div_clk[i].div.hw; + + lsp1_div_clk[i].div.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &lsp1_div_clk[i].div.hw); + if (ret) { + pr_warn("lsp1 clk %s init error!\n", + lsp1_div_clk[i].div.hw.init->name); + } + } + + if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp1_hw_onecell_data)) + panic("could not register clk provider\n"); + pr_info("lsp1-clk init over, nr:%d\n", LSP1_NR_CLKS); + + return 0; +} + +static const struct of_device_id zx_clkc_match_table[] = { + { .compatible = "zte,zx296718-topcrm", .data = &top_clocks_init }, + { .compatible = "zte,zx296718-lsp0crm", .data = &lsp0_clocks_init }, + { .compatible = "zte,zx296718-lsp1crm", .data = &lsp1_clocks_init }, + { } +}; + +static int zx_clkc_probe(struct platform_device *pdev) +{ + int (*init_fn)(struct device_node *np); + struct device_node *np = pdev->dev.of_node; + + init_fn = of_device_get_match_data(&pdev->dev); + if (!init_fn) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + return init_fn(np); +} + +static struct platform_driver zx_clk_driver = { + .probe = zx_clkc_probe, + .driver = { + .name = "zx296718-clkc", + .of_match_table = zx_clkc_match_table, + }, +}; + +static int __init zx_clk_init(void) +{ + return platform_driver_register(&zx_clk_driver); +} +core_initcall(zx_clk_init); diff --git a/drivers/clk/zte/clk.c b/drivers/clk/zte/clk.c index 7c73c53..c4c1251 100644 --- a/drivers/clk/zte/clk.c +++ b/drivers/clk/zte/clk.c @@ -21,8 +21,8 @@ #define to_clk_zx_audio(_hw) container_of(_hw, struct clk_zx_audio, hw) #define CFG0_CFG1_OFFSET 4 -#define LOCK_FLAG BIT(30) -#define POWER_DOWN BIT(31) +#define LOCK_FLAG 30 +#define POWER_DOWN 31 static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate) { @@ -50,8 +50,8 @@ static int hw_to_idx(struct clk_zx_pll *zx_pll) hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET); /* For matching the value in lookup table */ - hw_cfg0 &= ~LOCK_FLAG; - hw_cfg0 |= POWER_DOWN; + hw_cfg0 &= ~BIT(zx_pll->lock_bit); + hw_cfg0 |= BIT(zx_pll->pd_bit); for (i = 0; i < zx_pll->count; i++) { if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1) @@ -108,10 +108,10 @@ static int zx_pll_enable(struct clk_hw *hw) u32 reg; reg = readl_relaxed(zx_pll->reg_base); - writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base); + writel_relaxed(reg & ~BIT(zx_pll->pd_bit), zx_pll->reg_base); return readl_relaxed_poll_timeout(zx_pll->reg_base, reg, - reg & LOCK_FLAG, 0, 100); + reg & BIT(zx_pll->lock_bit), 0, 100); } static void zx_pll_disable(struct clk_hw *hw) @@ -120,7 +120,7 @@ static void zx_pll_disable(struct clk_hw *hw) u32 reg; reg = readl_relaxed(zx_pll->reg_base); - writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base); + writel_relaxed(reg | BIT(zx_pll->pd_bit), zx_pll->reg_base); } static int zx_pll_is_enabled(struct clk_hw *hw) @@ -130,10 +130,10 @@ static int zx_pll_is_enabled(struct clk_hw *hw) reg = readl_relaxed(zx_pll->reg_base); - return !(reg & POWER_DOWN); + return !(reg & BIT(zx_pll->pd_bit)); } -static const struct clk_ops zx_pll_ops = { +const struct clk_ops zx_pll_ops = { .recalc_rate = zx_pll_recalc_rate, .round_rate = zx_pll_round_rate, .set_rate = zx_pll_set_rate, @@ -141,6 +141,7 @@ static const struct clk_ops zx_pll_ops = { .disable = zx_pll_disable, .is_enabled = zx_pll_is_enabled, }; +EXPORT_SYMBOL(zx_pll_ops); struct clk *clk_register_zx_pll(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg_base, @@ -164,6 +165,8 @@ struct clk *clk_register_zx_pll(const char *name, const char *parent_name, zx_pll->reg_base = reg_base; zx_pll->lookup_table = lookup_table; zx_pll->count = count; + zx_pll->lock_bit = LOCK_FLAG; + zx_pll->pd_bit = POWER_DOWN; zx_pll->lock = lock; zx_pll->hw.init = &init; diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h index 65ae08b..0df3474 100644 --- a/drivers/clk/zte/clk.h +++ b/drivers/clk/zte/clk.h @@ -12,6 +12,26 @@ #include <linux/clk-provider.h> #include <linux/spinlock.h> +#define PNAME(x) static const char *x[] + +#define CLK_HW_INIT(_name, _parent, _ops, _flags) \ + &(struct clk_init_data) { \ + .flags = _flags, \ + .name = _name, \ + .parent_names = (const char *[]) { _parent }, \ + .num_parents = 1, \ + .ops = _ops, \ + } + +#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \ + &(struct clk_init_data) { \ + .flags = _flags, \ + .name = _name, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .ops = _ops, \ + } + struct zx_pll_config { unsigned long rate; u32 cfg0; @@ -24,8 +44,115 @@ struct clk_zx_pll { const struct zx_pll_config *lookup_table; /* order by rate asc */ int count; spinlock_t *lock; + u8 pd_bit; /* power down bit */ + u8 lock_bit; /* pll lock flag bit */ +}; + +#define PLL_RATE(_rate, _cfg0, _cfg1) \ +{ \ + .rate = _rate, \ + .cfg0 = _cfg0, \ + .cfg1 = _cfg1, \ +} + +#define ZX_PLL(_name, _parent, _reg, _table, _pd, _lock) \ +{ \ + .reg_base = (void __iomem *) _reg, \ + .lookup_table = _table, \ + .count = ARRAY_SIZE(_table), \ + .pd_bit = _pd, \ + .lock_bit = _lock, \ + .hw.init = CLK_HW_INIT(_name, _parent, &zx_pll_ops, \ + CLK_GET_RATE_NOCACHE), \ +} + +#define ZX296718_PLL(_name, _parent, _reg, _table) \ +ZX_PLL(_name, _parent, _reg, _table, 0, 30) + +struct zx_clk_gate { + struct clk_gate gate; + u16 id; +}; + +#define GATE(_id, _name, _parent, _reg, _bit, _flag, _gflags) \ +{ \ + .gate = { \ + .reg = (void __iomem *) _reg, \ + .bit_idx = (_bit), \ + .flags = _gflags, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &clk_gate_ops, \ + _flag | CLK_IGNORE_UNUSED), \ + }, \ + .id = _id, \ +} + +struct zx_clk_fixed_factor { + struct clk_fixed_factor factor; + u16 id; +}; + +#define FFACTOR(_id, _name, _parent, _mult, _div, _flag) \ +{ \ + .factor = { \ + .div = _div, \ + .mult = _mult, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &clk_fixed_factor_ops, \ + _flag), \ + }, \ + .id = _id, \ +} + +struct zx_clk_mux { + struct clk_mux mux; + u16 id; +}; + +#define MUX_F(_id, _name, _parent, _reg, _shift, _width, _flag, _mflag) \ +{ \ + .mux = { \ + .reg = (void __iomem *) _reg, \ + .mask = BIT(_width) - 1, \ + .shift = _shift, \ + .flags = _mflag, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parent, \ + &clk_mux_ops, \ + _flag), \ + }, \ + .id = _id, \ +} + +#define MUX(_id, _name, _parent, _reg, _shift, _width) \ +MUX_F(_id, _name, _parent, _reg, _shift, _width, 0, 0) + +struct zx_clk_div { + struct clk_divider div; + u16 id; }; +#define DIV_T(_id, _name, _parent, _reg, _shift, _width, _flag, _table) \ +{ \ + .div = { \ + .reg = (void __iomem *) _reg, \ + .shift = _shift, \ + .width = _width, \ + .flags = 0, \ + .table = _table, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &clk_divider_ops, \ + _flag), \ + }, \ + .id = _id, \ +} + struct clk *clk_register_zx_pll(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg_base, const struct zx_pll_config *lookup_table, int count, spinlock_t *lock); @@ -38,4 +165,6 @@ struct clk_zx_audio { struct clk *clk_register_zx_audio(const char *name, const char * const parent_name, unsigned long flags, void __iomem *reg_base); + +extern const struct clk_ops zx_pll_ops; #endif |